pg_dump failed to handle backslashes embedded in function definitions
authorTom Lane
Thu, 4 Jan 2001 01:23:47 +0000 (01:23 +0000)
committerTom Lane
Thu, 4 Jan 2001 01:23:47 +0000 (01:23 +0000)
(and most other places where it needed to output a string literal, too,
except for data INSERT statements).  Per bug report from Easter, 12/1/00.

src/bin/pg_dump/pg_dump.c

index 588f90df5497275f5ade3e8f078e17ce472af265..43c91e3e4ed3f89472d04ec6b2b66a279f461f3a 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.183 2000/12/03 20:45:37 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.184 2001/01/04 01:23:47 tgl Exp $
  *
  * Modifications - 6/10/96 - [email protected] - version 1.13.dhb
  *
@@ -138,7 +138,7 @@ static void dumpTriggers(Archive *fout, const char *tablename,
             TableInfo *tblinfo, int numTables);
 static void dumpRules(Archive *fout, const char *tablename,
          TableInfo *tblinfo, int numTables);
-static char *checkForQuote(const char *s);
+static void formatStringLiteral(PQExpBuffer buf, const char *str);
 static void clearTableInfo(TableInfo *, int);
 static void dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
            TypeInfo *tinfo, int numTypes);
@@ -434,7 +434,6 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
    PQExpBuffer q = createPQExpBuffer();
    int         tuple;
    int         field;
-   const char *expsrc;
 
    appendPQExpBuffer(q, "SELECT * FROM %s", fmtId(classname, force_quotes));
    res = PQexec(g_conn, q->data);
@@ -492,32 +491,10 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
                    /*
                     * All other types are printed as string literals,
                     * with appropriate escaping of special characters.
-                    * Quote mark ' goes to '' per SQL standard, other
-                    * stuff goes to \ sequences.
                     */
-                   archputc('\'', fout);
-                   expsrc = PQgetvalue(res, tuple, field);
-                   while (*expsrc)
-                   {
-                       char        ch = *expsrc++;
-
-                       if (ch == '\\' || ch == '\'')
-                       {
-                           archputc(ch, fout);         /* double these */
-                           archputc(ch, fout);
-                       }
-                       else if (ch < '\040')
-                       {
-                           /* generate octal escape for control chars */
-                           archputc('\\', fout);
-                           archputc(((ch >> 6) & 3) + '0', fout);
-                           archputc(((ch >> 3) & 7) + '0', fout);
-                           archputc((ch & 7) + '0', fout);
-                       }
-                       else
-                           archputc(ch, fout);
-                   }
-                   archputc('\'', fout);
+                   resetPQExpBuffer(q);
+                   formatStringLiteral(q, PQgetvalue(res, tuple, field));
+                   archprintf(fout, "%s", q->data);
                    break;
            }
        }
@@ -527,6 +504,41 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
    return 1;
 }
 
+/*
+ * Convert a string value to an SQL string literal,
+ * with appropriate escaping of special characters.
+ * Quote mark ' goes to '' per SQL standard, other
+ * stuff goes to \ sequences.
+ * The literal is appended to the given PQExpBuffer.
+ */
+static void
+formatStringLiteral(PQExpBuffer buf, const char *str)
+{
+   appendPQExpBufferChar(buf, '\'');
+   while (*str)
+   {
+       char    ch = *str++;
+
+       if (ch == '\\' || ch == '\'')
+       {
+           appendPQExpBufferChar(buf, ch); /* double these */
+           appendPQExpBufferChar(buf, ch);
+       }
+       else if ((unsigned char) ch < (unsigned char) ' ' &&
+                ch != '\n' && ch != '\t')
+       {
+           /* 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');
+       }
+       else
+           appendPQExpBufferChar(buf, ch);
+   }
+   appendPQExpBufferChar(buf, '\'');
+}
+
 /*
  * DumpClasses -
  *   dump the contents of all the classes.
@@ -1067,7 +1079,8 @@ dumpDatabase(Archive *AH)
 
    /* Get the dba */
    appendPQExpBuffer(dbQry, "select (select usename from pg_user where datdba = usesysid) as dba from pg_database"
-                           " where datname = '%s'", PQdb(g_conn));
+                           " where datname = ");
+   formatStringLiteral(dbQry, PQdb(g_conn));
 
    res = PQexec(g_conn, dbQry->data);
    if (!res ||
@@ -1826,7 +1839,7 @@ getFuncs(int *numFuncs)
        finfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
        finfo[i].proname = strdup(PQgetvalue(res, i, i_proname));
 
-       finfo[i].prosrc = checkForQuote(PQgetvalue(res, i, i_prosrc));
+       finfo[i].prosrc = strdup(PQgetvalue(res, i, i_prosrc));
        finfo[i].probin = strdup(PQgetvalue(res, i, i_probin));
 
        finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype));
@@ -1955,7 +1968,9 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
            PGresult   *res2;
 
            resetPQExpBuffer(query);
-           appendPQExpBuffer(query, "SELECT pg_get_viewdef('%s') as viewdef ", tblinfo[i].relname);
+           appendPQExpBuffer(query, "SELECT pg_get_viewdef(");
+           formatStringLiteral(query, tblinfo[i].relname);
+           appendPQExpBuffer(query, ") as viewdef");
            res2 = PQexec(g_conn, query->data);
            if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK)
            {
@@ -2753,8 +2768,9 @@ dumpComment(Archive *fout, const char *target, const char *oid)
    {
        i_description = PQfnumber(res, "description");
        resetPQExpBuffer(query);
-       appendPQExpBuffer(query, "COMMENT ON %s IS '%s';\n",
-                           target, checkForQuote(PQgetvalue(res, 0, i_description)));
+       appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
+       formatStringLiteral(query, PQgetvalue(res, 0, i_description));
+       appendPQExpBuffer(query, ";\n");
 
        ArchiveEntry(fout, oid, target, "COMMENT", NULL, query->data, "" /*Del*/,
                       "" /* Copy */, "" /*Owner*/, NULL, NULL);    
@@ -2788,8 +2804,8 @@ dumpDBComment(Archive *fout)
    /*** Build query to find comment ***/
 
    query = createPQExpBuffer();
-   appendPQExpBuffer(query, "SELECT oid FROM pg_database WHERE datname = '%s'",
-                     PQdb(g_conn));
+   appendPQExpBuffer(query, "SELECT oid FROM pg_database WHERE datname = ");
+   formatStringLiteral(query, PQdb(g_conn));
 
    /*** Execute query ***/
 
@@ -2864,25 +2880,28 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
        resetPQExpBuffer(q);
        appendPQExpBuffer(q,
                          "CREATE TYPE %s "
-              "( internallength = %s, externallength = %s, input = %s, "
-                 "output = %s, send = %s, receive = %s, default = '%s'",
+                         "( internallength = %s, externallength = %s,",
                          fmtId(tinfo[i].typname, force_quotes),
                          tinfo[i].typlen,
-                         tinfo[i].typprtlen,
-                         tinfo[i].typinput,
-                         tinfo[i].typoutput,
-                         tinfo[i].typsend,
-                         tinfo[i].typreceive,
-                         tinfo[i].typdefault);
+                         tinfo[i].typprtlen);
+       /* cannot combine these because fmtId uses static result area */
+       appendPQExpBuffer(q, " input = %s,",
+                         fmtId(tinfo[i].typinput, force_quotes));
+       appendPQExpBuffer(q, " output = %s,",
+                         fmtId(tinfo[i].typoutput, force_quotes));
+       appendPQExpBuffer(q, " send = %s,",
+                         fmtId(tinfo[i].typsend, force_quotes));
+       appendPQExpBuffer(q, " receive = %s, default = ",
+                         fmtId(tinfo[i].typreceive, force_quotes));
+       formatStringLiteral(q, tinfo[i].typdefault);
 
        if (tinfo[i].isArray)
        {
            char       *elemType;
 
            elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem, zeroAsOpaque);
-
-           appendPQExpBuffer(q, ", element = %s, delimiter = '%s'",
-                             elemType, tinfo[i].typdelim);
+           appendPQExpBuffer(q, ", element = %s, delimiter = ", elemType);
+           formatStringLiteral(q, tinfo[i].typdelim);
        }
        if (tinfo[i].passedbyvalue)
            appendPQExpBuffer(q, ",passedbyvalue);\n");
@@ -2971,24 +2990,25 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
 
        dumpOneFunc(fout, finfo, fidx, tinfo, numTypes);
 
-       lanname = checkForQuote(PQgetvalue(res, i, i_lanname));
-       lancompiler = checkForQuote(PQgetvalue(res, i, i_lancompiler));
+       lanname = PQgetvalue(res, i, i_lanname);
+       lancompiler = PQgetvalue(res, i, i_lancompiler);
 
-       appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE '%s';\n", lanname);
+       appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE ");
+       formatStringLiteral(delqry, lanname);
+       appendPQExpBuffer(delqry, ";\n");
 
-       appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE '%s' "
-               "HANDLER %s LANCOMPILER '%s';\n",
-               (PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ? "TRUSTED " : "",
-               lanname,
-               fmtId(finfo[fidx].proname, force_quotes),
-               lancompiler);
+       appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE ",
+                         (PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ?
+                         "TRUSTED " : "");
+       formatStringLiteral(defqry, lanname);
+       appendPQExpBuffer(defqry, " HANDLER %s LANCOMPILER ",
+                         fmtId(finfo[fidx].proname, force_quotes));
+       formatStringLiteral(defqry, lancompiler);
+       appendPQExpBuffer(defqry, ";\n");
 
        ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE",
                NULL, defqry->data, delqry->data, "", "", NULL, NULL);
 
-       free(lanname);
-       free(lancompiler);
-
        resetPQExpBuffer(defqry);
        resetPQExpBuffer(delqry);
    }
@@ -3071,15 +3091,21 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
     */
    if (strcmp(finfo[i].probin, "-") != 0)
    {
+       appendPQExpBuffer(asPart, "AS ");
+       formatStringLiteral(asPart, finfo[i].probin);
        if (strcmp(finfo[i].prosrc, "-") != 0)
-           appendPQExpBuffer(asPart, "AS '%s', '%s'", finfo[i].probin, finfo[i].prosrc);
-       else
-           appendPQExpBuffer(asPart, "AS '%s'", finfo[i].probin);
+       {
+           appendPQExpBuffer(asPart, ", ");
+           formatStringLiteral(asPart, finfo[i].prosrc);
+       }
    }
    else
    {
        if (strcmp(finfo[i].prosrc, "-") != 0)
-           appendPQExpBuffer(asPart, "AS '%s'", finfo[i].prosrc);
+       {
+           appendPQExpBuffer(asPart, "AS ");
+           formatStringLiteral(asPart, finfo[i].prosrc);
+       }
    }
 
    strcpy(func_lang, PQgetvalue(res, 0, i_lanname));
@@ -3107,10 +3133,11 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
 
    resetPQExpBuffer(q);
    appendPQExpBuffer(q, "CREATE FUNCTION %s ", fn->data );
-   appendPQExpBuffer(q, "RETURNS %s%s %s LANGUAGE '%s'",
-                     (finfo[i].retset) ? " SETOF " : "",
+   appendPQExpBuffer(q, "RETURNS %s%s %s LANGUAGE ",
+                     (finfo[i].retset) ? "SETOF " : "",
                      findTypeByOid(tinfo, numTypes, finfo[i].prorettype, zeroAsOpaque),
-                     asPart->data, func_lang);
+                     asPart->data);
+   formatStringLiteral(q, func_lang);
 
    if (finfo[i].iscachable || finfo[i].isstrict) /* OR in new attrs here */
    {
@@ -3286,8 +3313,10 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs,
                          findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype, zeroAsOpaque + useBaseTypeName));
 
        if (agginfo[i].agginitval)
-           appendPQExpBuffer(details, ", INITCOND = '%s'",
-                             agginfo[i].agginitval);
+       {
+           appendPQExpBuffer(details, ", INITCOND = ");
+           formatStringLiteral(details, agginfo[i].agginitval);
+       }
 
        if (!(strcmp(agginfo[i].aggfinalfn, "-") == 0))
            appendPQExpBuffer(details, ", FINALFUNC = %s",
@@ -3970,7 +3999,8 @@ findLastBuiltinOid(const char* dbname)
    PQExpBuffer query = createPQExpBuffer();
 
    resetPQExpBuffer(query);
-   appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = '%s'", dbname);
+   appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
+   formatStringLiteral(query, dbname);
 
    res = PQexec(g_conn, query->data);
    if (res == NULL ||
@@ -3999,41 +4029,6 @@ findLastBuiltinOid(const char* dbname)
 }
 
 
-/*
- * checkForQuote:
- *   checks a string for quote characters and quotes them
- */
-static char *
-checkForQuote(const char *s)
-{
-   char       *r;
-   char        c;
-   char       *result;
-
-   int         j = 0;
-
-   r = malloc(strlen(s) * 3 + 1);      /* definitely long enough */
-
-   while ((c = *s) != '\0')
-   {
-
-       if (c == '\'')
-       {
-           r[j++] = '\'';      /* quote the single quotes */
-       }
-       r[j++] = c;
-       s++;
-   }
-   r[j] = '\0';
-
-   result = strdup(r);
-   free(r);
-
-   return result;
-
-}
-
-
 static void
 dumpSequence(Archive *fout, TableInfo tbinfo)
 {
@@ -4113,8 +4108,9 @@ dumpSequence(Archive *fout, TableInfo tbinfo)
 
 
    resetPQExpBuffer(query);
-   appendPQExpBuffer(query, "SELECT setval ('%s', %d, '%c');\n", 
-                       fmtId(tbinfo.relname, force_quotes), last, called);
+   appendPQExpBuffer(query, "SELECT setval (");
+   formatStringLiteral(query, fmtId(tbinfo.relname, force_quotes));
+   appendPQExpBuffer(query, ", %d, '%c');\n", last, called);
 
    ArchiveEntry(fout, tbinfo.oid, fmtId(tbinfo.relname, force_quotes), "SEQUENCE SET", NULL,
                    query->data, "" /* Del */, "", "", NULL, NULL);
@@ -4191,12 +4187,13 @@ dumpRules(Archive *fout, const char *tablename,
                          "   (select usename from pg_user where pg_class.relowner = usesysid) AS viewowner, "
                          "   pg_rewrite.oid, pg_rewrite.rulename "
                          "FROM pg_rewrite, pg_class, pg_rules "
-                         "WHERE pg_class.relname = '%s' "
+                         "WHERE pg_class.relname = ");
+       formatStringLiteral(query, tblinfo[t].relname);
+       appendPQExpBuffer(query,
                          "    AND pg_rewrite.ev_class = pg_class.oid "
                          "    AND pg_rules.tablename = pg_class.relname "
                          "    AND pg_rules.rulename = pg_rewrite.rulename "
-                         "ORDER BY pg_rewrite.oid",
-                         tblinfo[t].relname);
+                         "ORDER BY pg_rewrite.oid");
        res = PQexec(g_conn, query->data);
        if (!res ||
            PQresultStatus(res) != PGRES_TUPLES_OK)