From 134b463f027d1113df6f983c3348f165b1ac1ffa Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 28 May 2006 21:13:54 +0000
Subject: [PATCH] Fix up pg_dump to do string escaping fully correctly for
 client encoding and standard_conforming_strings; likewise for the other
 client programs that need it.  As per previous discussion, a pg_dump dump now
 conforms to the standard_conforming_strings setting of the source database.
 We don't use E'' syntax in the dump, thereby improving portability of the
 SQL.  I added a SET escape_strings_warning = off command to keep the dumps
 from getting a lot of back-chatter from that.

---
 src/backend/utils/adt/quote.c        |  27 +++--
 src/backend/utils/adt/ruleutils.c    |  48 +++-----
 src/bin/pg_dump/dumputils.c          | 143 ++++++++++++----------
 src/bin/pg_dump/dumputils.h          |   9 +-
 src/bin/pg_dump/pg_backup.h          |   9 +-
 src/bin/pg_dump/pg_backup_archiver.c |  97 +++++++++++----
 src/bin/pg_dump/pg_backup_archiver.h |   5 +-
 src/bin/pg_dump/pg_dump.c            | 170 +++++++++++----------------
 src/bin/pg_dump/pg_dumpall.c         |  50 +++++---
 src/bin/psql/describe.c              |  70 ++++++-----
 src/bin/psql/large_obj.c             |  19 +--
 src/bin/scripts/createdb.c           |   7 +-
 src/bin/scripts/createuser.c         |  12 +-
 src/include/c.h                      |  11 +-
 src/interfaces/libpq/fe-exec.c       |   5 +-
 15 files changed, 378 insertions(+), 304 deletions(-)

diff --git a/src/backend/utils/adt/quote.c b/src/backend/utils/adt/quote.c
index 31991ee51ed..6da6c3df3d6 100644
--- a/src/backend/utils/adt/quote.c
+++ b/src/backend/utils/adt/quote.c
@@ -7,14 +7,13 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/quote.c,v 1.19 2006/05/26 23:48:54 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/quote.c,v 1.20 2006/05/28 21:13:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "utils/builtins.h"
-#include "parser/gramparse.h"
 
 
 /*
@@ -49,6 +48,12 @@ quote_ident(PG_FUNCTION_ARGS)
 /*
  * quote_literal -
  *	  returns a properly quoted literal
+ *
+ * NOTE: think not to make this function's behavior change with
+ * standard_conforming_strings.  We don't know where the result
+ * literal will be used, and so we must generate a result that
+ * will work with either setting.  Take a look at what dblink
+ * uses this for before thinking you know better.
  */
 Datum
 quote_literal(PG_FUNCTION_ARGS)
@@ -66,20 +71,22 @@ quote_literal(PG_FUNCTION_ARGS)
 	cp1 = VARDATA(t);
 	cp2 = VARDATA(result);
 
-	if (!standard_conforming_strings)
-		for (; len-- > 0; cp1++)
-			if (*cp1 == '\\')
-			{
-				*cp2++ = ESCAPE_STRING_SYNTAX;
-				break;
-			}
+	for (; len-- > 0; cp1++)
+	{
+		if (*cp1 == '\\')
+		{
+			*cp2++ = ESCAPE_STRING_SYNTAX;
+			break;
+		}
+	}
 
 	len = VARSIZE(t) - VARHDRSZ;
 	cp1 = VARDATA(t);
+
 	*cp2++ = '\'';
 	while (len-- > 0)
 	{
-		if (SQL_STR_DOUBLE(*cp1, !standard_conforming_strings))
+		if (SQL_STR_DOUBLE(*cp1, true))
 			*cp2++ = *cp1;
 		*cp2++ = *cp1++;
 	}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8a06f7bcb66..83697436c0e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2,7 +2,7 @@
  * ruleutils.c	- Functions to convert stored expressions/querytrees
  *				back to source text
  *
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.222 2006/05/26 23:48:54 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.223 2006/05/28 21:13:53 tgl Exp $
  **********************************************************************/
 
 #include "postgres.h"
@@ -534,18 +534,23 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
 		{
 			if (i > 0)
 				appendStringInfo(&buf, ", ");
-			if (!standard_conforming_strings && strchr(p, '\\') != NULL)
-				appendStringInfoChar(&buf, ESCAPE_STRING_SYNTAX);
+			/*
+			 * We form the string literal according to the prevailing setting
+			 * of standard_conforming_strings; we never use E''.
+			 * User is responsible for making sure result is used correctly.
+			 */
 			appendStringInfoChar(&buf, '\'');
-
 			while (*p)
 			{
-				if (SQL_STR_DOUBLE(*p, !standard_conforming_strings))
-					appendStringInfoChar(&buf, *p);
-				appendStringInfoChar(&buf, *p++);
+				char		ch = *p++;
+
+				if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
+					appendStringInfoChar(&buf, ch);
+				appendStringInfoChar(&buf, ch);
 			}
-			p++;
 			appendStringInfoChar(&buf, '\'');
+			/* advance p to next string embedded in tgargs */
+			p++;
 		}
 	}
 
@@ -3883,8 +3888,7 @@ get_const_expr(Const *constval, deparse_context *context)
 	char	   *valptr;
 	bool		isfloat = false;
 	bool		needlabel;
-	bool		is_e_string = false;
-	
+
 	if (constval->constisnull)
 	{
 		/*
@@ -3946,32 +3950,18 @@ get_const_expr(Const *constval, deparse_context *context)
 		default:
 
 			/*
-			 * We must quote any funny characters in the constant's
-			 * representation. XXX Any MULTIBYTE considerations here?
+			 * We form the string literal according to the prevailing setting
+			 * of standard_conforming_strings; we never use E''.
+			 * User is responsible for making sure result is used correctly.
 			 */
-			for (valptr = extval; *valptr; valptr++)
-				if ((!standard_conforming_strings && *valptr == '\\') ||
-					(unsigned char) *valptr < (unsigned char) ' ')
-				{
-					appendStringInfoChar(buf, ESCAPE_STRING_SYNTAX);
-					is_e_string = true;
-					break;
-				}
-
 			appendStringInfoChar(buf, '\'');
 			for (valptr = extval; *valptr; valptr++)
 			{
 				char		ch = *valptr;
 
-				if (SQL_STR_DOUBLE(ch, is_e_string))
-				{
-					appendStringInfoChar(buf, ch);
-					appendStringInfoChar(buf, ch);
-				}
-				else if ((unsigned char) ch < (unsigned char) ' ')
-					appendStringInfo(buf, "\\%03o", (int) ch);
-				else
+				if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
 					appendStringInfoChar(buf, ch);
+				appendStringInfoChar(buf, ch);
 			}
 			appendStringInfoChar(buf, '\'');
 			break;
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 795ce2b986d..bad85e2bda5 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.28 2006/05/26 23:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.29 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -100,63 +100,102 @@ fmtId(const char *rawid)
 
 /*
  * Convert a string value to an SQL string literal and append it to
- * the given buffer.
+ * the given buffer.  We assume the specified client_encoding and
+ * standard_conforming_strings settings.
  *
- * Special characters are escaped. Quote mark ' goes to '' per SQL
- * standard, other stuff goes to \ sequences.  If escapeAll is false,
- * whitespace characters are not escaped (tabs, newlines, etc.).  This
- * is appropriate for dump file output.  Using E'' strings for
- * backslashes is always safe for standard_conforming_strings on or off.
+ * This is essentially equivalent to libpq's PQescapeStringInternal,
+ * except for the output buffer structure.  We need it in situations
+ * where we do not have a PGconn available.  Where we do,
+ * appendStringLiteralConn is a better choice.
  */
 void
-appendStringLiteral(PQExpBuffer buf, const char *str, bool escapeAll,
-					bool e_string_for_backslash)
+appendStringLiteral(PQExpBuffer buf, const char *str,
+					int encoding, bool std_strings)
 {
-	char		ch;
-	const char *p;
-	bool		is_e_string = false;
+	size_t		length = strlen(str);
+	const char *source = str;
+	char	   *target;
 
-	for (p = str; *p; p++)
+	if (!enlargePQExpBuffer(buf, 2 * length + 2))
+		return;
+
+	target = buf->data + buf->len;
+	*target++ = '\'';
+
+	while (*source != '\0')
 	{
-		ch = *p;
+		char	c = *source;
+		int		len;
+		int		i;
 
-		if ((e_string_for_backslash && ch == '\\') ||
-			((unsigned char) ch < (unsigned char) ' ' &&
-			 (escapeAll ||
-			  (ch != '\t' && ch != '\n' && ch != '\v' &&
-			   ch != '\f' && ch != '\r'))))
+		/* Fast path for plain ASCII */
+		if (!IS_HIGHBIT_SET(c))
 		{
-			appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
-			is_e_string = true;
-			break;
+			/* Apply quoting if needed */
+			if (SQL_STR_DOUBLE(c, !std_strings))
+				*target++ = c;
+			/* Copy the character */
+			*target++ = c;
+			source++;
+			continue;
 		}
-	}
 
-	appendPQExpBufferChar(buf, '\'');
-	for (p = str; *p; p++)
-	{
-		ch = *p;
-		if (SQL_STR_DOUBLE(ch, is_e_string))
+		/* Slow path for possible multibyte characters */
+		len = PQmblen(source, encoding);
+
+		/* Copy the character */
+		for (i = 0; i < len; i++)
 		{
-			appendPQExpBufferChar(buf, ch);
-			appendPQExpBufferChar(buf, ch);
+			if (*source == '\0')
+				break;
+			*target++ = *source++;
 		}
-		else if ((unsigned char) ch < (unsigned char) ' ' &&
-				 (escapeAll ||
-				  (ch != '\t' && ch != '\n' && ch != '\v' &&
-				   ch != '\f' && ch != '\r')))
+
+		/*
+		 * If we hit premature end of string (ie, incomplete multibyte
+		 * character), try to pad out to the correct length with spaces.
+		 * We may not be able to pad completely, but we will always be able
+		 * to insert at least one pad space (since we'd not have quoted a
+		 * multibyte character).  This should be enough to make a string that
+		 * the server will error out on.
+		 */
+		if (i < len)
 		{
-			/*
-			 * generate octal escape for control chars other than whitespace
-			 */
-			appendPQExpBufferChar(buf, '\\');
-			appendPQExpBufferChar(buf, ((ch >> 6) & 3) + '0');
-			appendPQExpBufferChar(buf, ((ch >> 3) & 7) + '0');
-			appendPQExpBufferChar(buf, (ch & 7) + '0');
+			char   *stop = buf->data + buf->maxlen - 2;
+
+			for (; i < len; i++)
+			{
+				if (target >= stop)
+					break;
+				*target++ = ' ';
+			}
+			break;
 		}
-		else
-			appendPQExpBufferChar(buf, ch);
 	}
+
+	/* Write the terminating quote and NUL character. */
+	*target++ = '\'';
+	*target = '\0';
+
+	buf->len = target - buf->data;
+}
+
+
+/*
+ * Convert a string value to an SQL string literal and append it to
+ * the given buffer.  Encoding and string syntax rules are as indicated
+ * by current settings of the PGconn.
+ */
+void
+appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
+{
+	size_t	length = strlen(str);
+
+	if (!enlargePQExpBuffer(buf, 2 * length + 2))
+		return;
+	appendPQExpBufferChar(buf, '\'');
+	buf->len += PQescapeStringConn(conn, buf->data + buf->len,
+								   str, length, NULL);
 	appendPQExpBufferChar(buf, '\'');
 }
 
@@ -167,7 +206,8 @@ appendStringLiteral(PQExpBuffer buf, const char *str, bool escapeAll,
  * dollar quote delimiter will begin with that (after the opening $).
  *
  * No escaping is done at all on str, in compliance with the rules
- * for parsing dollar quoted strings.
+ * for parsing dollar quoted strings.  Also, we need not worry about
+ * encoding issues.
  */
 void
 appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
@@ -204,21 +244,6 @@ appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
 }
 
 
-/*
- * Use dollar quoting if the string to be quoted contains ' or \,
- * otherwise use standard quoting.
- */
-void
-appendStringLiteralDQOpt(PQExpBuffer buf, const char *str,
-						 bool escapeAll, const char *dqprefix)
-{
-	if (strchr(str, '\'') == NULL && strchr(str, '\\') == NULL)
-		appendStringLiteral(buf, str, escapeAll, true);
-	else
-		appendStringLiteralDQ(buf, str, dqprefix);
-}
-
-
 /*
  * Convert backend's version string into a number.
  */
diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h
index 73bc91d6a66..7492d42f7e4 100644
--- a/src/bin/pg_dump/dumputils.h
+++ b/src/bin/pg_dump/dumputils.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.16 2006/05/26 23:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.17 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,15 +15,16 @@
 #ifndef DUMPUTILS_H
 #define DUMPUTILS_H
 
+#include "libpq-fe.h"
 #include "pqexpbuffer.h"
 
 extern const char *fmtId(const char *identifier);
 extern void appendStringLiteral(PQExpBuffer buf, const char *str,
-					bool escapeAll, bool e_string_for_backslash);
+								int encoding, bool std_strings);
+extern void appendStringLiteralConn(PQExpBuffer buf, const char *str,
+									PGconn *conn);
 extern void appendStringLiteralDQ(PQExpBuffer buf, const char *str,
 					  const char *dqprefix);
-extern void appendStringLiteralDQOpt(PQExpBuffer buf, const char *str,
-						 bool escapeAll, const char *dqprefix);
 extern int	parse_version(const char *versionString);
 extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
 extern bool buildACLCommands(const char *name, const char *type,
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 9faaac22289..9f0f7ca3ca1 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup.h,v 1.38 2006/02/12 04:04:32 momjian Exp $
+ *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup.h,v 1.39 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,6 +60,10 @@ typedef struct _Archive
 	int			minRemoteVersion;		/* allowable range */
 	int			maxRemoteVersion;
 
+	/* info needed for string escaping */
+	int			encoding;		/* libpq code for client_encoding */
+	bool		std_strings;	/* standard_conforming_strings */
+
 	/* error handling */
 	bool		exit_on_error;	/* whether to exit on SQL errors... */
 	int			n_errors;		/* number of errors (if no die) */
@@ -182,4 +186,7 @@ archprintf(Archive *AH, const char *fmt,...)
 /* This extension allows gcc to check the format string */
 __attribute__((format(printf, 2, 3)));
 
+#define appendStringLiteralAH(buf,str,AH) \
+	appendStringLiteral(buf, str, (AH)->encoding, (AH)->std_strings)
+
 #endif   /* PG_BACKUP_H */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 35042f30504..904de34a7c7 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.130 2006/05/26 23:48:54 momjian Exp $
+ *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.131 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 
 #include "pqexpbuffer.h"
 #include "libpq/libpq-fs.h"
+#include "mb/pg_wchar.h"
 
 
 const char *progname;
@@ -60,7 +61,8 @@ static void _becomeUser(ArchiveHandle *AH, const char *user);
 static void _becomeOwner(ArchiveHandle *AH, TocEntry *te);
 static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName);
 static void _selectTablespace(ArchiveHandle *AH, const char *tablespace);
-
+static void processEncodingEntry(ArchiveHandle *AH, TocEntry *te);
+static void processStdStringsEntry(ArchiveHandle *AH, TocEntry *te);
 static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls);
 static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
 static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
@@ -1589,6 +1591,14 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
 	AH->vmin = K_VERS_MINOR;
 	AH->vrev = K_VERS_REV;
 
+	/* initialize for backwards compatible string processing */
+	AH->public.encoding = PG_SQL_ASCII;
+	AH->public.std_strings = false;
+
+	/* sql error handling */
+	AH->public.exit_on_error = true;
+	AH->public.n_errors = 0;
+
 	AH->createDate = time(NULL);
 
 	AH->intSize = sizeof(int);
@@ -1676,10 +1686,6 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
 			die_horribly(AH, modulename, "unrecognized file format \"%d\"\n", fmt);
 	}
 
-	/* sql error handling */
-	AH->public.exit_on_error = true;
-	AH->public.n_errors = 0;
-
 	return AH;
 }
 
@@ -1888,11 +1894,62 @@ ReadToc(ArchiveHandle *AH)
 		ahlog(AH, 3, "read TOC entry %d (ID %d) for %s %s\n",
 			  i, te->dumpId, te->desc, te->tag);
 
+		/* link completed entry into TOC circular list */
 		te->prev = AH->toc->prev;
 		AH->toc->prev->next = te;
 		AH->toc->prev = te;
 		te->next = AH->toc;
+
+		/* special processing immediately upon read for some items */
+		if (strcmp(te->desc, "ENCODING") == 0)
+			processEncodingEntry(AH, te);
+		else if (strcmp(te->desc, "STDSTRINGS") == 0)
+			processStdStringsEntry(AH, te);
+	}
+}
+
+static void
+processEncodingEntry(ArchiveHandle *AH, TocEntry *te)
+{
+	/* te->defn should have the form SET client_encoding = 'foo'; */
+	char	   *defn = strdup(te->defn);
+	char	   *ptr1;
+	char	   *ptr2 = NULL;
+	int			encoding;
+
+	ptr1 = strchr(defn, '\'');
+	if (ptr1)
+		ptr2 = strchr(++ptr1, '\'');
+	if (ptr2)
+	{
+		*ptr2 = '\0';
+		encoding = pg_char_to_encoding(ptr1);
+		if (encoding < 0)
+			die_horribly(AH, modulename, "unrecognized encoding \"%s\"\n",
+						 ptr1);
+		AH->public.encoding = encoding;
 	}
+	else
+		die_horribly(AH, modulename, "invalid ENCODING item: %s\n",
+					 te->defn);
+
+	free(defn);
+}
+
+static void
+processStdStringsEntry(ArchiveHandle *AH, TocEntry *te)
+{
+	/* te->defn should have the form SET standard_conforming_strings = 'x'; */
+	char	   *ptr1;
+
+	ptr1 = strchr(te->defn, '\'');
+	if (ptr1 && strncmp(ptr1, "'on'", 4) == 0)
+		AH->public.std_strings = true;
+	else if (ptr1 && strncmp(ptr1, "'off'", 5) == 0)
+		AH->public.std_strings = false;
+	else
+		die_horribly(AH, modulename, "invalid STDSTRINGS item: %s\n",
+					 te->defn);
 }
 
 static teReqs
@@ -1900,10 +1957,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
 {
 	teReqs		res = REQ_ALL;
 
-	/* ENCODING and STDSTRINGS objects are dumped specially, so always reject */
-	if (strcmp(te->desc, "ENCODING") == 0)
-		return 0;
-	if (strcmp(te->desc, "STDSTRINGS") == 0)
+	/* ENCODING and STDSTRINGS items are dumped specially, so always reject */
+	if (strcmp(te->desc, "ENCODING") == 0 ||
+		strcmp(te->desc, "STDSTRINGS") == 0)
 		return 0;
 
 	/* If it's an ACL, maybe ignore it */
@@ -2005,24 +2061,21 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
 static void
 _doSetFixedOutputState(ArchiveHandle *AH)
 {
-	TocEntry   *te;
+	/* Select the correct character set encoding */
+	ahprintf(AH, "SET client_encoding = '%s';\n",
+			 pg_encoding_to_char(AH->public.encoding));
 
-	/* If we have an encoding or std_strings setting, emit that */
-	te = AH->toc->next;
-	while (te != AH->toc)
-	{
-		if (strcmp(te->desc, "ENCODING") == 0)
-			ahprintf(AH, "%s", te->defn);
-		if (strcmp(te->desc, "STDSTRINGS") == 0)
-			ahprintf(AH, "%s", te->defn);
-		te = te->next;
-	}
+	/* Select the correct string literal syntax */
+	ahprintf(AH, "SET standard_conforming_strings = %s;\n",
+			 AH->public.std_strings ? "on" : "off");
 
 	/* Make sure function checking is disabled */
 	ahprintf(AH, "SET check_function_bodies = false;\n");
 
 	/* Avoid annoying notices etc */
 	ahprintf(AH, "SET client_min_messages = warning;\n");
+	if (!AH->public.std_strings)
+		ahprintf(AH, "SET escape_string_warning = off;\n");
 
 	ahprintf(AH, "\n");
 }
@@ -2043,7 +2096,7 @@ _doSetSessionAuth(ArchiveHandle *AH, const char *user)
 	 * SQL requires a string literal here.	Might as well be correct.
 	 */
 	if (user && *user)
-		appendStringLiteral(cmd, user, false, true);
+		appendStringLiteralAHX(cmd, user, AH);
 	else
 		appendPQExpBuffer(cmd, "DEFAULT");
 	appendPQExpBuffer(cmd, ";");
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 1a71cbec0c3..1b6f6a1d8fa 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -17,7 +17,7 @@
  *
  *
  * IDENTIFICATION
- *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.69 2006/02/05 20:58:47 tgl Exp $
+ *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.70 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -326,6 +326,9 @@ extern void WriteDataChunks(ArchiveHandle *AH);
 extern teReqs TocIDRequired(ArchiveHandle *AH, DumpId id, RestoreOptions *ropt);
 extern bool checkSeek(FILE *fp);
 
+#define appendStringLiteralAHX(buf,str,AH) \
+	appendStringLiteral(buf, str, (AH)->public.encoding, (AH)->public.std_strings)
+
 /*
  * Mandatory routines for each supported format
  */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index a32af2846ac..daf7829b511 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
  *	by PostgreSQL
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.435 2006/05/28 17:23:29 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.436 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,6 +57,7 @@ int			optreset;
 
 #include "libpq-fe.h"
 #include "libpq/libpq-fs.h"
+#include "mb/pg_wchar.h"
 
 #include "pg_dump.h"
 #include "pg_backup.h"
@@ -82,7 +83,6 @@ bool		g_verbose;			/* User wants verbose narration of our
 								 * activities. */
 Archive    *g_fout;				/* the script file */
 PGconn	   *g_conn;				/* the database connection */
-bool		std_strings = false;		/* GUC variable */
 
 /* various user-settable parameters */
 bool		dumpInserts;		/* dump data using proper insert strings */
@@ -194,6 +194,7 @@ main(int argc, char **argv)
 	const char *pgport = NULL;
 	const char *username = NULL;
 	const char *dumpencoding = NULL;
+	const char *std_strings;
 	bool		oids = false;
 	TableInfo  *tblinfo;
 	int			numTables;
@@ -517,25 +518,35 @@ main(int argc, char **argv)
 	g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport,
 							 username, force_password, ignore_version);
 
+	/* Set the client encoding if requested */
+	if (dumpencoding)
+	{
+		if (PQsetClientEncoding(g_conn, dumpencoding) < 0)
+		{
+			write_msg(NULL, "invalid client encoding \"%s\" specified\n",
+					  dumpencoding);
+			exit(1);
+		}
+	}
+
 	/*
-	 * Start serializable transaction to dump consistent data.
+	 * Get the active encoding and the standard_conforming_strings setting,
+	 * so we know how to escape strings.
 	 */
-	do_sql_command(g_conn, "BEGIN");
+	g_fout->encoding = PQclientEncoding(g_conn);
 
-	do_sql_command(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
+	std_strings = PQparameterStatus(g_conn, "standard_conforming_strings");
+	g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
 
 	/* Set the datestyle to ISO to ensure the dump's portability */
 	do_sql_command(g_conn, "SET DATESTYLE = ISO");
 
-	/* Set the client encoding */
-	if (dumpencoding)
-	{
-		char	   *cmd = malloc(strlen(dumpencoding) + 32);
+	/*
+	 * Start serializable transaction to dump consistent data.
+	 */
+	do_sql_command(g_conn, "BEGIN");
 
-		sprintf(cmd, "SET client_encoding='%s'", dumpencoding);
-		do_sql_command(g_conn, cmd);
-		free(cmd);
-	}
+	do_sql_command(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
 
 	/* Select the appropriate subquery to convert user IDs to names */
 	if (g_fout->remoteVersion >= 80100)
@@ -618,12 +629,11 @@ main(int argc, char **argv)
 	 * order.
 	 */
 
-	/* First the special encoding entry. */
+	/* First the special ENCODING and STDSTRINGS entries. */
 	dumpEncoding(g_fout);
-
 	dumpStdStrings(g_fout);
 
-	/* The database item is always second, unless we don't want it at all */
+	/* The database item is always next, unless we don't want it at all */
 	if (!dataOnly && selectTableName == NULL && selectSchemaName == NULL)
 		dumpDatabase(g_fout);
 
@@ -1088,7 +1098,9 @@ dumpTableData_insert(Archive *fout, void *dcontext)
 					default:
 						/* All other types are printed as string literals. */
 						resetPQExpBuffer(q);
-						appendStringLiteral(q, PQgetvalue(res, tuple, field), false, !std_strings);
+						appendStringLiteralAH(q,
+											  PQgetvalue(res, tuple, field),
+											  fout);
 						archputs(q->data, fout);
 						break;
 				}
@@ -1240,7 +1252,7 @@ dumpDatabase(Archive *AH)
 						  "FROM pg_database "
 						  "WHERE datname = ",
 						  username_subquery);
-		appendStringLiteral(dbQry, datname, true, !std_strings);
+		appendStringLiteralAH(dbQry, datname, AH);
 	}
 	else if (g_fout->remoteVersion >= 80000)
 	{
@@ -1251,7 +1263,7 @@ dumpDatabase(Archive *AH)
 						  "FROM pg_database "
 						  "WHERE datname = ",
 						  username_subquery);
-		appendStringLiteral(dbQry, datname, true, !std_strings);
+		appendStringLiteralAH(dbQry, datname, AH);
 	}
 	else if (g_fout->remoteVersion >= 70100)
 	{
@@ -1262,7 +1274,7 @@ dumpDatabase(Archive *AH)
 						  "FROM pg_database "
 						  "WHERE datname = ",
 						  username_subquery);
-		appendStringLiteral(dbQry, datname, true, !std_strings);
+		appendStringLiteralAH(dbQry, datname, AH);
 	}
 	else
 	{
@@ -1275,7 +1287,7 @@ dumpDatabase(Archive *AH)
 						  "FROM pg_database "
 						  "WHERE datname = ",
 						  username_subquery);
-		appendStringLiteral(dbQry, datname, true, !std_strings);
+		appendStringLiteralAH(dbQry, datname, AH);
 	}
 
 	res = PQexec(g_conn, dbQry->data);
@@ -1314,7 +1326,7 @@ dumpDatabase(Archive *AH)
 	if (strlen(encoding) > 0)
 	{
 		appendPQExpBuffer(creaQry, " ENCODING = ");
-		appendStringLiteral(creaQry, encoding, true, !std_strings);
+		appendStringLiteralAH(creaQry, encoding, AH);
 	}
 	if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0)
 		appendPQExpBuffer(creaQry, " TABLESPACE = %s",
@@ -1353,7 +1365,7 @@ dumpDatabase(Archive *AH)
 		if (comment && strlen(comment)) {
 			resetPQExpBuffer(dbQry);
 			appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", fmtId(datname));
-			appendStringLiteral(dbQry, comment, false, !std_strings);
+			appendStringLiteralAH(dbQry, comment, AH);
 			appendPQExpBuffer(dbQry, ";\n");
 
 			ArchiveEntry(AH, dbCatId, createDumpId(), datname, NULL, NULL,
@@ -1381,28 +1393,14 @@ dumpDatabase(Archive *AH)
 static void
 dumpEncoding(Archive *AH)
 {
-	PQExpBuffer qry;
-	PGresult   *res;
-
-	/* Can't read the encoding from pre-7.3 servers (SHOW isn't a query) */
-	if (AH->remoteVersion < 70300)
-		return;
+	const char *encname = pg_encoding_to_char(AH->encoding);
+	PQExpBuffer qry = createPQExpBuffer();
 
 	if (g_verbose)
-		write_msg(NULL, "saving encoding\n");
-
-	qry = createPQExpBuffer();
-
-	appendPQExpBuffer(qry, "SHOW client_encoding");
-
-	res = PQexec(g_conn, qry->data);
-
-	check_sql_result(res, g_conn, qry->data, PGRES_TUPLES_OK);
-
-	resetPQExpBuffer(qry);
+		write_msg(NULL, "saving encoding = %s\n", encname);
 
 	appendPQExpBuffer(qry, "SET client_encoding = ");
-	appendStringLiteral(qry, PQgetvalue(res, 0, 0), true, !std_strings);
+	appendStringLiteralAH(qry, encname, AH);
 	appendPQExpBuffer(qry, ";\n");
 
 	ArchiveEntry(AH, nilCatalogId, createDumpId(),
@@ -1411,8 +1409,6 @@ dumpEncoding(Archive *AH)
 				 NULL, 0,
 				 NULL, NULL);
 
-	PQclear(res);
-
 	destroyPQExpBuffer(qry);
 }
 
@@ -1423,39 +1419,16 @@ dumpEncoding(Archive *AH)
 static void
 dumpStdStrings(Archive *AH)
 {
-	PQExpBuffer qry;
-	PGresult   *res;
+	const char *stdstrings = AH->std_strings ? "on" : "off";
+	PQExpBuffer qry = createPQExpBuffer();
 
 	if (g_verbose)
-		write_msg(NULL, "saving standard_conforming_strings setting\n");
-
-	qry = createPQExpBuffer();
-
-	/* standard_conforming_strings not used in pre-8.2 servers */
-	if (AH->remoteVersion < 80200)
-	{
-		appendPQExpBuffer(qry, "SET standard_conforming_strings = 'off';\n");
-		std_strings = false;
-	}
-	else
-	{
-		appendPQExpBuffer(qry, "SHOW standard_conforming_strings");
+		write_msg(NULL, "saving standard_conforming_strings = %s\n",
+				  stdstrings);
 
-		res = PQexec(g_conn, qry->data);
+	appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
+					  stdstrings);
 
-		check_sql_result(res, g_conn, qry->data, PGRES_TUPLES_OK);
-
-		resetPQExpBuffer(qry);
-
-		std_strings = (strcmp(PQgetvalue(res, 0, 0), "on") == 0);
-		appendPQExpBuffer(qry, "SET standard_conforming_strings = ");
-		appendStringLiteral(qry, PQgetvalue(res, 0, 0), true, !std_strings);
-		appendPQExpBuffer(qry, ";\n");
-		puts(PQgetvalue(res, 0, 0));
-
-		PQclear(res);
-	}
-	
 	ArchiveEntry(AH, nilCatalogId, createDumpId(),
 				 "STDSTRINGS", NULL, NULL, "",
 				 false, "STDSTRINGS", qry->data, "", NULL,
@@ -1639,7 +1612,7 @@ dumpBlobComments(Archive *AH, void *arg)
 
 			printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ",
 							  blobOid);
-			appendStringLiteral(commentcmd, comment, false, !std_strings);
+			appendStringLiteralAH(commentcmd, comment, AH);
 			appendPQExpBuffer(commentcmd, ";\n");
 
 			archputs(commentcmd->data, AH);
@@ -4393,7 +4366,7 @@ dumpComment(Archive *fout, const char *target,
 		PQExpBuffer query = createPQExpBuffer();
 
 		appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
-		appendStringLiteral(query, comments->descr, false, !std_strings);
+		appendStringLiteralAH(query, comments->descr, fout);
 		appendPQExpBuffer(query, ";\n");
 
 		ArchiveEntry(fout, nilCatalogId, createDumpId(),
@@ -4451,7 +4424,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo,
 
 			resetPQExpBuffer(query);
 			appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
-			appendStringLiteral(query, descr, false, !std_strings);
+			appendStringLiteralAH(query, descr, fout);
 			appendPQExpBuffer(query, ";\n");
 
 			ArchiveEntry(fout, nilCatalogId, createDumpId(),
@@ -4473,7 +4446,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo,
 
 			resetPQExpBuffer(query);
 			appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
-			appendStringLiteral(query, descr, false, !std_strings);
+			appendStringLiteralAH(query, descr, fout);
 			appendPQExpBuffer(query, ";\n");
 
 			ArchiveEntry(fout, nilCatalogId, createDumpId(),
@@ -5039,7 +5012,7 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	{
 		appendPQExpBuffer(q, ",\n    DEFAULT = ");
 		if (typdefault_is_literal)
-			appendStringLiteral(q, typdefault, true, !std_strings);
+			appendStringLiteralAH(q, typdefault, fout);
 		else
 			appendPQExpBufferStr(q, typdefault);
 	}
@@ -5058,7 +5031,7 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	if (typdelim && strcmp(typdelim, ",") != 0)
 	{
 		appendPQExpBuffer(q, ",\n    DELIMITER = ");
-		appendStringLiteral(q, typdelim, true, !std_strings);
+		appendStringLiteralAH(q, typdelim, fout);
 	}
 
 	if (strcmp(typalign, "c") == 0)
@@ -5173,7 +5146,7 @@ dumpDomain(Archive *fout, TypeInfo *tinfo)
 	{
 		appendPQExpBuffer(q, " DEFAULT ");
 		if (typdefault_is_literal)
-			appendStringLiteral(q, typdefault, true, !std_strings);
+			appendStringLiteralAH(q, typdefault, fout);
 		else
 			appendPQExpBufferStr(q, typdefault);
 	}
@@ -5743,7 +5716,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 	if (strcmp(probin, "-") != 0)
 	{
 		appendPQExpBuffer(asPart, "AS ");
-		appendStringLiteral(asPart, probin, true, !std_strings);
+		appendStringLiteralAH(asPart, probin, fout);
 		if (strcmp(prosrc, "-") != 0)
 		{
 			appendPQExpBuffer(asPart, ", ");
@@ -5752,10 +5725,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 			 * where we have bin, use dollar quoting if allowed and src
 			 * contains quote or backslash; else use regular quoting.
 			 */
-			if (disable_dollar_quoting)
-				appendStringLiteral(asPart, prosrc, false, !std_strings);
+			if (disable_dollar_quoting ||
+				(strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
+				appendStringLiteralAH(asPart, prosrc, fout);
 			else
-				appendStringLiteralDQOpt(asPart, prosrc, false, NULL);
+				appendStringLiteralDQ(asPart, prosrc, NULL);
 		}
 	}
 	else
@@ -5765,7 +5739,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 			appendPQExpBuffer(asPart, "AS ");
 			/* with no bin, dollar quote src unconditionally if allowed */
 			if (disable_dollar_quoting)
-				appendStringLiteral(asPart, prosrc, false, !std_strings);
+				appendStringLiteralAH(asPart, prosrc, fout);
 			else
 				appendStringLiteralDQ(asPart, prosrc, NULL);
 		}
@@ -6681,9 +6655,9 @@ dumpConversion(Archive *fout, ConvInfo *convinfo)
 	appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
 					  (condefault) ? "DEFAULT " : "",
 					  fmtId(convinfo->dobj.name));
-	appendStringLiteral(q, conforencoding, true, !std_strings);
+	appendStringLiteralAH(q, conforencoding, fout);
 	appendPQExpBuffer(q, " TO ");
-	appendStringLiteral(q, contoencoding, true, !std_strings);
+	appendStringLiteralAH(q, contoencoding, fout);
 	/* regproc is automatically quoted in 7.3 and above */
 	appendPQExpBuffer(q, " FROM %s;\n", conproc);
 
@@ -6924,7 +6898,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	if (!PQgetisnull(res, 0, i_agginitval))
 	{
 		appendPQExpBuffer(details, ",\n    INITCOND = ");
-		appendStringLiteral(details, agginitval, true, !std_strings);
+		appendStringLiteralAH(details, agginitval, fout);
 	}
 
 	if (strcmp(aggfinalfn, "-") != 0)
@@ -7111,7 +7085,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
 		{
 			appendPQExpBuffer(query, "SELECT definition as viewdef "
 							  " from pg_views where viewname = ");
-			appendStringLiteral(query, tbinfo->dobj.name, true, !std_strings);
+			appendStringLiteralAH(query, tbinfo->dobj.name, fout);
 			appendPQExpBuffer(query, ";");
 		}
 
@@ -7760,7 +7734,7 @@ findLastBuiltinOid_V71(const char *dbname)
 
 	resetPQExpBuffer(query);
 	appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
-	appendStringLiteral(query, dbname, true, !std_strings);
+	appendStringLiteralAH(query, dbname, g_fout);
 
 	res = PQexec(g_conn, query->data);
 	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
@@ -7961,13 +7935,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			(owning_tab = findTableByOid(tbinfo->owning_tab)) != NULL)
 		{
 			appendPQExpBuffer(query, "pg_catalog.pg_get_serial_sequence(");
-			appendStringLiteral(query, fmtId(owning_tab->dobj.name), true, !std_strings);
+			appendStringLiteralAH(query, fmtId(owning_tab->dobj.name), fout);
 			appendPQExpBuffer(query, ", ");
-			appendStringLiteral(query, owning_tab->attnames[tbinfo->owning_col - 1], true, !std_strings);
+			appendStringLiteralAH(query, owning_tab->attnames[tbinfo->owning_col - 1], fout);
 			appendPQExpBuffer(query, ")");
 		}
 		else
-			appendStringLiteral(query, fmtId(tbinfo->dobj.name), true, !std_strings);
+			appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
 		appendPQExpBuffer(query, ", %s, %s);\n",
 						  last, (called ? "true" : "false"));
 
@@ -8100,8 +8074,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
 	p = tginfo->tgargs;
 	for (findx = 0; findx < tginfo->tgnargs; findx++)
 	{
-		const char *s = p,
-				   *s2 = p;
+		const char *s = p;
 
 		/* Set 'p' to end of arg string. marked by '\000' */
 		for (;;)
@@ -8126,13 +8099,6 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
 		}
 		p--;
 
-		while (s2 < p)
-			if (*s2++ == '\\')
-			{
-				appendPQExpBufferChar(query, ESCAPE_STRING_SYNTAX);
-				break;
-			}
-
 		appendPQExpBufferChar(query, '\'');
 		while (s < p)
 		{
@@ -8142,7 +8108,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
 			 *	bytea unconditionally doubles backslashes, so we suppress
 			 *	the doubling for standard_conforming_strings.
 			 */
-			if (std_strings && *s == '\\')
+			if (fout->std_strings && *s == '\\' && s[1] == '\\')
 				s++;
 			appendPQExpBufferChar(query, *s++);
 		}
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index cb03fbda07b..74c2e88271b 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.76 2006/05/26 23:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.77 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,7 @@ int			optreset;
 #include "libpq-fe.h"
 #include "pg_backup.h"
 #include "pqexpbuffer.h"
+#include "mb/pg_wchar.h"
 
 
 /* version string we expect back from pg_dump */
@@ -53,7 +54,8 @@ static void dumpTablespaces(PGconn *conn);
 static void dumpCreateDB(PGconn *conn);
 static void dumpDatabaseConfig(PGconn *conn, const char *dbname);
 static void dumpUserConfig(PGconn *conn, const char *username);
-static void makeAlterConfigCommand(const char *arrayitem, const char *type, const char *name);
+static void makeAlterConfigCommand(PGconn *conn, const char *arrayitem,
+								   const char *type, const char *name);
 static void dumpDatabases(PGconn *conn);
 static void dumpTimestamp(char *msg);
 
@@ -88,6 +90,8 @@ main(int argc, char *argv[])
 	bool		globals_only = false;
 	bool		schema_only = false;
 	PGconn	   *conn;
+	int			encoding;
+	const char *std_strings;
 	int			c,
 				ret;
 
@@ -313,6 +317,15 @@ main(int argc, char *argv[])
 		conn = connectDatabase("template1", pghost, pgport, pguser,
 							   force_password, true);
 
+	/*
+	 * Get the active encoding and the standard_conforming_strings setting,
+	 * so we know how to escape strings.
+	 */
+	encoding = PQclientEncoding(conn);
+	std_strings = PQparameterStatus(conn, "standard_conforming_strings");
+	if (!std_strings)
+		std_strings = "off";
+
 	printf("--\n-- PostgreSQL database cluster dump\n--\n\n");
 	if (verbose)
 		dumpTimestamp("Started on");
@@ -321,6 +334,12 @@ main(int argc, char *argv[])
 
 	if (!data_only)
 	{
+		/* Replicate encoding and std_strings in output */
+		printf("SET client_encoding = '%s';\n",
+			   pg_encoding_to_char(encoding));
+		printf("SET standard_conforming_strings = %s;\n", std_strings);
+		printf("\n");
+
 		/* Dump roles (users) */
 		dumpRoles(conn);
 
@@ -535,7 +554,7 @@ dumpRoles(PGconn *conn)
 		if (!PQgetisnull(res, i, i_rolpassword))
 		{
 			appendPQExpBuffer(buf, " PASSWORD ");
-			appendStringLiteral(buf, PQgetvalue(res, i, i_rolpassword), true, true);
+			appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolpassword), conn);
 		}
 
 		if (!PQgetisnull(res, i, i_rolvaliduntil))
@@ -546,7 +565,7 @@ dumpRoles(PGconn *conn)
 
 		if (!PQgetisnull(res, i, i_rolcomment)) {
 			appendPQExpBuffer(buf, "COMMENT ON ROLE %s IS ", fmtId(rolename));
-			appendStringLiteral(buf, PQgetvalue(res, i, i_rolcomment), true, true);
+			appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolcomment), conn);
 			appendPQExpBuffer(buf, ";\n");
 		}
 
@@ -730,7 +749,7 @@ dumpTablespaces(PGconn *conn)
 		appendPQExpBuffer(buf, " OWNER %s", fmtId(spcowner));
 
 		appendPQExpBuffer(buf, " LOCATION ");
-		appendStringLiteral(buf, spclocation, true, true);
+		appendStringLiteralConn(buf, spclocation, conn);
 		appendPQExpBuffer(buf, ";\n");
 
 		if (!skip_acls &&
@@ -745,7 +764,7 @@ dumpTablespaces(PGconn *conn)
 
 		if (spccomment && strlen(spccomment)) {
 			appendPQExpBuffer(buf, "COMMENT ON TABLESPACE %s IS ", fspcname);
-			appendStringLiteral(buf, spccomment, true, true);
+			appendStringLiteralConn(buf, spccomment, conn);
 			appendPQExpBuffer(buf, ";\n");
 		}
 
@@ -868,7 +887,7 @@ dumpCreateDB(PGconn *conn)
 				appendPQExpBuffer(buf, " OWNER = %s", fmtId(dbowner));
 
 			appendPQExpBuffer(buf, " ENCODING = ");
-			appendStringLiteral(buf, dbencoding, true, true);
+			appendStringLiteralConn(buf, dbencoding, conn);
 
 			/* Output tablespace if it isn't default */
 			if (strcmp(dbtablespace, "pg_default") != 0)
@@ -884,7 +903,7 @@ dumpCreateDB(PGconn *conn)
 			if (strcmp(dbistemplate, "t") == 0)
 			{
 				appendPQExpBuffer(buf, "UPDATE pg_database SET datistemplate = 't' WHERE datname = ");
-				appendStringLiteral(buf, dbname, true, true);
+				appendStringLiteralConn(buf, dbname, conn);
 				appendPQExpBuffer(buf, ";\n");
 			}
 		}
@@ -929,13 +948,14 @@ dumpDatabaseConfig(PGconn *conn, const char *dbname)
 		PGresult   *res;
 
 		printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE datname = ", count);
-		appendStringLiteral(buf, dbname, true, true);
+		appendStringLiteralConn(buf, dbname, conn);
 		appendPQExpBuffer(buf, ";");
 
 		res = executeQuery(conn, buf->data);
 		if (!PQgetisnull(res, 0, 0))
 		{
-			makeAlterConfigCommand(PQgetvalue(res, 0, 0), "DATABASE", dbname);
+			makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0),
+								   "DATABASE", dbname);
 			PQclear(res);
 			count++;
 		}
@@ -968,13 +988,14 @@ dumpUserConfig(PGconn *conn, const char *username)
 			printfPQExpBuffer(buf, "SELECT rolconfig[%d] FROM pg_authid WHERE rolname = ", count);
 		else
 			printfPQExpBuffer(buf, "SELECT useconfig[%d] FROM pg_shadow WHERE usename = ", count);
-		appendStringLiteral(buf, username, true, true);
+		appendStringLiteralConn(buf, username, conn);
 
 		res = executeQuery(conn, buf->data);
 		if (PQntuples(res) == 1 &&
 			!PQgetisnull(res, 0, 0))
 		{
-			makeAlterConfigCommand(PQgetvalue(res, 0, 0), "ROLE", username);
+			makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0),
+								   "ROLE", username);
 			PQclear(res);
 			count++;
 		}
@@ -994,7 +1015,8 @@ dumpUserConfig(PGconn *conn, const char *username)
  * Helper function for dumpXXXConfig().
  */
 static void
-makeAlterConfigCommand(const char *arrayitem, const char *type, const char *name)
+makeAlterConfigCommand(PGconn *conn, const char *arrayitem,
+					   const char *type, const char *name)
 {
 	char	   *pos;
 	char	   *mine;
@@ -1016,7 +1038,7 @@ makeAlterConfigCommand(const char *arrayitem, const char *type, const char *name
 		|| pg_strcasecmp(mine, "search_path") == 0)
 		appendPQExpBuffer(buf, "%s", pos + 1);
 	else
-		appendStringLiteral(buf, pos + 1, false, true);
+		appendStringLiteralConn(buf, pos + 1, conn);
 	appendPQExpBuffer(buf, ";\n");
 
 	printf("%s", buf->data);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 44d16a4e4eb..2159c5c715f 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3,13 +3,12 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.136 2006/05/28 02:27:08 alvherre Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.137 2006/05/28 21:13:54 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "describe.h"
 
-#include "libpq-fe.h"
-#include "pqexpbuffer.h"
+#include "dumputils.h"
 
 #include "common.h"
 #include "settings.h"
@@ -1815,7 +1814,12 @@ processNamePattern(PQExpBuffer buf, const char *pattern,
 	 * Parse the pattern, converting quotes and lower-casing unquoted letters;
 	 * we assume this was NOT done by scan_option.	Also, adjust shell-style
 	 * wildcard characters into regexp notation.
+	 *
+	 * Note: the result of this pass is the actual regexp pattern we want
+	 * to execute.  Quoting/escaping it into a SQL literal will be done below.
 	 */
+	appendPQExpBufferChar(&namebuf, '^');
+
 	inquotes = false;
 	cp = pattern;
 
@@ -1854,6 +1858,7 @@ processNamePattern(PQExpBuffer buf, const char *pattern,
 			resetPQExpBuffer(&schemabuf);
 			appendPQExpBufferStr(&schemabuf, namebuf.data);
 			resetPQExpBuffer(&namebuf);
+			appendPQExpBufferChar(&namebuf, '^');
 			cp++;
 		}
 		else
@@ -1869,14 +1874,9 @@ processNamePattern(PQExpBuffer buf, const char *pattern,
 			 */
 			if ((inquotes || force_escape) &&
 				strchr("|*+?()[]{}.^$\\", *cp))
-				appendPQExpBuffer(&namebuf, "\\\\");
-
-			/* Ensure chars special to string literals are passed properly */
-			if (SQL_STR_DOUBLE(*cp, true))
-				appendPQExpBufferChar(&namebuf, *cp);
-
+				appendPQExpBufferChar(&namebuf, '\\');
 			i = PQmblen(cp, pset.encoding);
-			while (i--)
+			while (i-- && *cp)
 			{
 				appendPQExpBufferChar(&namebuf, *cp);
 				cp++;
@@ -1885,49 +1885,61 @@ processNamePattern(PQExpBuffer buf, const char *pattern,
 	}
 
 	/*
-	 * Now decide what we need to emit.
+	 * Now decide what we need to emit.  Note there will be a leading '^'
+	 * in the patterns in any case.
 	 */
-	if (namebuf.len > 0)
+	if (namebuf.len > 1)
 	{
 		/* We have a name pattern, so constrain the namevar(s) */
 
 		appendPQExpBufferChar(&namebuf, '$');
 		/* Optimize away ".*$", and possibly the whole pattern */
-		if (namebuf.len >= 3 &&
+		if (namebuf.len >= 4 &&
 			strcmp(namebuf.data + (namebuf.len - 3), ".*$") == 0)
-			namebuf.data[namebuf.len - 3] = '\0';
+		{
+			namebuf.len -= 3;
+			namebuf.data[namebuf.len] = '\0';
+		}
 
-		if (namebuf.data[0])
+		if (namebuf.len > 1)
 		{
 			WHEREAND();
 			if (altnamevar)
-				appendPQExpBuffer(buf,
-								  "(%s ~ '^%s'\n"
-								  "        OR %s ~ '^%s')\n",
-								  namevar, namebuf.data,
-								  altnamevar, namebuf.data);
+			{
+				appendPQExpBuffer(buf, "(%s ~ ", namevar);
+				appendStringLiteralConn(buf, namebuf.data, pset.db);
+				appendPQExpBuffer(buf, "\n        OR %s ~ ", altnamevar);
+				appendStringLiteralConn(buf, namebuf.data, pset.db);
+				appendPQExpBuffer(buf, ")\n");
+			}
 			else
-				appendPQExpBuffer(buf,
-								  "%s ~ '^%s'\n",
-								  namevar, namebuf.data);
+			{
+				appendPQExpBuffer(buf, "%s ~ ", namevar);
+				appendStringLiteralConn(buf, namebuf.data, pset.db);
+				appendPQExpBufferChar(buf, '\n');
+			}
 		}
 	}
 
-	if (schemabuf.len > 0)
+	if (schemabuf.len > 1)
 	{
 		/* We have a schema pattern, so constrain the schemavar */
 
 		appendPQExpBufferChar(&schemabuf, '$');
 		/* Optimize away ".*$", and possibly the whole pattern */
-		if (schemabuf.len >= 3 &&
+		if (schemabuf.len >= 4 &&
 			strcmp(schemabuf.data + (schemabuf.len - 3), ".*$") == 0)
-			schemabuf.data[schemabuf.len - 3] = '\0';
+		{
+			schemabuf.len -= 3;
+			schemabuf.data[schemabuf.len] = '\0';
+		}
 
-		if (schemabuf.data[0] && schemavar)
+		if (schemabuf.len > 1 && schemavar)
 		{
 			WHEREAND();
-			appendPQExpBuffer(buf, "%s ~ '^%s'\n",
-							  schemavar, schemabuf.data);
+			appendPQExpBuffer(buf, "%s ~ ", schemavar);
+			appendStringLiteralConn(buf, schemabuf.data, pset.db);
+			appendPQExpBufferChar(buf, '\n');
 		}
 	}
 	else
diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c
index f4b107538ac..5de0348b397 100644
--- a/src/bin/psql/large_obj.c
+++ b/src/bin/psql/large_obj.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/large_obj.c,v 1.42 2006/05/26 23:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/large_obj.c,v 1.43 2006/05/28 21:13:54 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "large_obj.h"
@@ -148,7 +148,6 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
 	PGresult   *res;
 	Oid			loid;
 	char		oidbuf[32];
-	unsigned int i;
 	bool		own_transaction;
 
 	if (!start_lo_xact("\\lo_import", &own_transaction))
@@ -171,21 +170,9 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
 		cmdbuf = malloc(slen * 2 + 256);
 		if (!cmdbuf)
 			return fail_lo_xact("\\lo_import", own_transaction);
-		sprintf(cmdbuf,
-				"COMMENT ON LARGE OBJECT %u IS ",
-				loid);
+		sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
 		bufptr = cmdbuf + strlen(cmdbuf);
-
-		if (strchr(comment_arg, '\\') != NULL)
-			*bufptr++ = ESCAPE_STRING_SYNTAX;
-
-		*bufptr++ = '\'';
-		for (i = 0; i < slen; i++)
-		{
-			if (SQL_STR_DOUBLE(comment_arg[i], true))
-				*bufptr++ = comment_arg[i];
-			*bufptr++ = comment_arg[i];
-		}
+		bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
 		strcpy(bufptr, "'");
 
 		if (!(res = PSQLexec(cmdbuf, false)))
diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index 7413cdec52c..ec022b12036 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/scripts/createdb.c,v 1.17 2006/05/26 23:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/scripts/createdb.c,v 1.18 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -183,11 +183,12 @@ main(int argc, char *argv[])
 
 	if (comment)
 	{
+		conn = connectDatabase(dbname, host, port, username, password, progname);
+
 		printfPQExpBuffer(&sql, "COMMENT ON DATABASE %s IS ", fmtId(dbname));
-		appendStringLiteral(&sql, comment, false, true);
+		appendStringLiteralConn(&sql, comment, conn);
 		appendPQExpBuffer(&sql, ";\n");
 
-		conn = connectDatabase(dbname, host, port, username, password, progname);
 		if (echo)
 			printf("%s", sql.data);
 		result = PQexec(conn, sql.data);
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index 0d816b02e93..e4afdfb40ba 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/scripts/createuser.c,v 1.28 2006/05/26 23:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/scripts/createuser.c,v 1.29 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -236,6 +236,8 @@ main(int argc, char *argv[])
 	if (login == 0)
 		login = TRI_YES;
 
+	conn = connectDatabase("postgres", host, port, username, password, progname);
+
 	initPQExpBuffer(&sql);
 
 	printfPQExpBuffer(&sql, "CREATE ROLE %s", fmtId(newuser));
@@ -252,17 +254,17 @@ main(int argc, char *argv[])
 			char	   *encrypted_password;
 
 			encrypted_password = PQencryptPassword(newpassword,
-															newuser);
+												   newuser);
 			if (!encrypted_password)
 			{
 				fprintf(stderr, _("Password encryption failed.\n"));
 				exit(1);
 			}
-			appendStringLiteral(&sql, encrypted_password, false, true);
+			appendStringLiteralConn(&sql, encrypted_password, conn);
 			PQfreemem(encrypted_password);
 		}
 		else
-			appendStringLiteral(&sql, newpassword, false, true);
+			appendStringLiteralConn(&sql, newpassword, conn);
 	}
 	if (superuser == TRI_YES)
 		appendPQExpBuffer(&sql, " SUPERUSER");
@@ -288,8 +290,6 @@ main(int argc, char *argv[])
 		appendPQExpBuffer(&sql, " CONNECTION LIMIT %s", conn_limit);
 	appendPQExpBuffer(&sql, ";\n");
 
-	conn = connectDatabase("postgres", host, port, username, password, progname);
-
 	if (echo)
 		printf("%s", sql.data);
 	result = PQexec(conn, sql.data);
diff --git a/src/include/c.h b/src/include/c.h
index 284367547ed..679981ea6cd 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/c.h,v 1.201 2006/05/26 23:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/c.h,v 1.202 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -473,12 +473,13 @@ typedef NameData *Name;
 #define NameStr(name)	((name).data)
 
 /*
- *	In 8.2, we are warning for \ in a non-E string if std_strings are off.
- *	For this reason, we use E for \ strings, unless standard_conforming_strings
- *	is on.
+ * Support macros for escaping strings.  escape_backslash should be TRUE
+ * if generating a non-standard-conforming string.  Prefixing a string
+ * with ESCAPE_STRING_SYNTAX guarantees it is non-standard-conforming.
+ * Beware of multiple evaluation of the "ch" argument!
  */
 #define SQL_STR_DOUBLE(ch, escape_backslash)	\
-					((ch) == '\'' || ((escape_backslash) && (ch) == '\\'))
+	((ch) == '\'' || ((ch) == '\\' && (escape_backslash)))
 	
 #define ESCAPE_STRING_SYNTAX	'E'
 
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 71ba05fbca6..69da7bc34c5 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.185 2006/05/28 17:23:29 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.186 2006/05/28 21:13:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2437,8 +2437,7 @@ PQescapeStringInternal(PGconn *conn,
 		if (!IS_HIGHBIT_SET(c))
 		{
 			/* Apply quoting if needed */
-			if (c == '\'' ||
-				(c == '\\' && !std_strings))
+			if (SQL_STR_DOUBLE(c, !std_strings))
 				*target++ = c;
 			/* Copy the character */
 			*target++ = c;
-- 
GitLab