From 134b463f027d1113df6f983c3348f165b1ac1ffa Mon Sep 17 00:00:00 2001 From: Tom Lane 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 31991ee51e..6da6c3df3d 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 8a06f7bcb6..83697436c0 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 795ce2b986..bad85e2bda 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 73bc91d6a6..7492d42f7e 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 9faaac2228..9f0f7ca3ca 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 35042f3050..904de34a7c 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 1a71cbec0c..1b6f6a1d8f 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 a32af2846a..daf7829b51 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 cb03fbda07..74c2e88271 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 44d16a4e4e..2159c5c715 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 f4b107538a..5de0348b39 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 7413cdec52..ec022b1203 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 0d816b02e9..e4afdfb40b 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 284367547e..679981ea6c 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 71ba05fbca..69da7bc34c 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; -- 2.40.0