pg_dump failed to handle backslashes embedded in function definitions

(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.
This commit is contained in:
Tom Lane 2001-01-04 01:23:47 +00:00
parent 60500d58bc
commit ea608bfd3b

View File

@ -22,7 +22,7 @@
* *
* *
* IDENTIFICATION * 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 - dave@bensoft.com - version 1.13.dhb * Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
* *
@ -138,7 +138,7 @@ static void dumpTriggers(Archive *fout, const char *tablename,
TableInfo *tblinfo, int numTables); TableInfo *tblinfo, int numTables);
static void dumpRules(Archive *fout, const char *tablename, static void dumpRules(Archive *fout, const char *tablename,
TableInfo *tblinfo, int numTables); 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 clearTableInfo(TableInfo *, int);
static void dumpOneFunc(Archive *fout, FuncInfo *finfo, int i, static void dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
TypeInfo *tinfo, int numTypes); TypeInfo *tinfo, int numTypes);
@ -434,7 +434,6 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
PQExpBuffer q = createPQExpBuffer(); PQExpBuffer q = createPQExpBuffer();
int tuple; int tuple;
int field; int field;
const char *expsrc;
appendPQExpBuffer(q, "SELECT * FROM %s", fmtId(classname, force_quotes)); appendPQExpBuffer(q, "SELECT * FROM %s", fmtId(classname, force_quotes));
res = PQexec(g_conn, q->data); 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, * All other types are printed as string literals,
* with appropriate escaping of special characters. * with appropriate escaping of special characters.
* Quote mark ' goes to '' per SQL standard, other
* stuff goes to \ sequences.
*/ */
archputc('\'', fout); resetPQExpBuffer(q);
expsrc = PQgetvalue(res, tuple, field); formatStringLiteral(q, PQgetvalue(res, tuple, field));
while (*expsrc) archprintf(fout, "%s", q->data);
{
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);
break; break;
} }
} }
@ -527,6 +504,41 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
return 1; 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 - * DumpClasses -
* dump the contents of all the classes. * dump the contents of all the classes.
@ -1067,7 +1079,8 @@ dumpDatabase(Archive *AH)
/* Get the dba */ /* Get the dba */
appendPQExpBuffer(dbQry, "select (select usename from pg_user where datdba = usesysid) as dba from pg_database" 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); res = PQexec(g_conn, dbQry->data);
if (!res || if (!res ||
@ -1826,7 +1839,7 @@ getFuncs(int *numFuncs)
finfo[i].oid = strdup(PQgetvalue(res, i, i_oid)); finfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
finfo[i].proname = strdup(PQgetvalue(res, i, i_proname)); 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].probin = strdup(PQgetvalue(res, i, i_probin));
finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype)); finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype));
@ -1955,7 +1968,9 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
PGresult *res2; PGresult *res2;
resetPQExpBuffer(query); 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); res2 = PQexec(g_conn, query->data);
if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK) 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"); i_description = PQfnumber(res, "description");
resetPQExpBuffer(query); resetPQExpBuffer(query);
appendPQExpBuffer(query, "COMMENT ON %s IS '%s';\n", appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
target, checkForQuote(PQgetvalue(res, 0, i_description))); formatStringLiteral(query, PQgetvalue(res, 0, i_description));
appendPQExpBuffer(query, ";\n");
ArchiveEntry(fout, oid, target, "COMMENT", NULL, query->data, "" /*Del*/, ArchiveEntry(fout, oid, target, "COMMENT", NULL, query->data, "" /*Del*/,
"" /* Copy */, "" /*Owner*/, NULL, NULL); "" /* Copy */, "" /*Owner*/, NULL, NULL);
@ -2788,8 +2804,8 @@ dumpDBComment(Archive *fout)
/*** Build query to find comment ***/ /*** Build query to find comment ***/
query = createPQExpBuffer(); query = createPQExpBuffer();
appendPQExpBuffer(query, "SELECT oid FROM pg_database WHERE datname = '%s'", appendPQExpBuffer(query, "SELECT oid FROM pg_database WHERE datname = ");
PQdb(g_conn)); formatStringLiteral(query, PQdb(g_conn));
/*** Execute query ***/ /*** Execute query ***/
@ -2864,25 +2880,28 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
resetPQExpBuffer(q); resetPQExpBuffer(q);
appendPQExpBuffer(q, appendPQExpBuffer(q,
"CREATE TYPE %s " "CREATE TYPE %s "
"( internallength = %s, externallength = %s, input = %s, " "( internallength = %s, externallength = %s,",
"output = %s, send = %s, receive = %s, default = '%s'",
fmtId(tinfo[i].typname, force_quotes), fmtId(tinfo[i].typname, force_quotes),
tinfo[i].typlen, tinfo[i].typlen,
tinfo[i].typprtlen, tinfo[i].typprtlen);
tinfo[i].typinput, /* cannot combine these because fmtId uses static result area */
tinfo[i].typoutput, appendPQExpBuffer(q, " input = %s,",
tinfo[i].typsend, fmtId(tinfo[i].typinput, force_quotes));
tinfo[i].typreceive, appendPQExpBuffer(q, " output = %s,",
tinfo[i].typdefault); 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) if (tinfo[i].isArray)
{ {
char *elemType; char *elemType;
elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem, zeroAsOpaque); elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem, zeroAsOpaque);
appendPQExpBuffer(q, ", element = %s, delimiter = ", elemType);
appendPQExpBuffer(q, ", element = %s, delimiter = '%s'", formatStringLiteral(q, tinfo[i].typdelim);
elemType, tinfo[i].typdelim);
} }
if (tinfo[i].passedbyvalue) if (tinfo[i].passedbyvalue)
appendPQExpBuffer(q, ",passedbyvalue);\n"); appendPQExpBuffer(q, ",passedbyvalue);\n");
@ -2971,24 +2990,25 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
dumpOneFunc(fout, finfo, fidx, tinfo, numTypes); dumpOneFunc(fout, finfo, fidx, tinfo, numTypes);
lanname = checkForQuote(PQgetvalue(res, i, i_lanname)); lanname = PQgetvalue(res, i, i_lanname);
lancompiler = checkForQuote(PQgetvalue(res, i, i_lancompiler)); 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' " appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE ",
"HANDLER %s LANCOMPILER '%s';\n", (PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ?
(PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ? "TRUSTED " : "", "TRUSTED " : "");
lanname, formatStringLiteral(defqry, lanname);
fmtId(finfo[fidx].proname, force_quotes), appendPQExpBuffer(defqry, " HANDLER %s LANCOMPILER ",
lancompiler); fmtId(finfo[fidx].proname, force_quotes));
formatStringLiteral(defqry, lancompiler);
appendPQExpBuffer(defqry, ";\n");
ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE", ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE",
NULL, defqry->data, delqry->data, "", "", NULL, NULL); NULL, defqry->data, delqry->data, "", "", NULL, NULL);
free(lanname);
free(lancompiler);
resetPQExpBuffer(defqry); resetPQExpBuffer(defqry);
resetPQExpBuffer(delqry); resetPQExpBuffer(delqry);
} }
@ -3071,15 +3091,21 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
*/ */
if (strcmp(finfo[i].probin, "-") != 0) if (strcmp(finfo[i].probin, "-") != 0)
{ {
appendPQExpBuffer(asPart, "AS ");
formatStringLiteral(asPart, finfo[i].probin);
if (strcmp(finfo[i].prosrc, "-") != 0) if (strcmp(finfo[i].prosrc, "-") != 0)
appendPQExpBuffer(asPart, "AS '%s', '%s'", finfo[i].probin, finfo[i].prosrc); {
else appendPQExpBuffer(asPart, ", ");
appendPQExpBuffer(asPart, "AS '%s'", finfo[i].probin); formatStringLiteral(asPart, finfo[i].prosrc);
}
} }
else else
{ {
if (strcmp(finfo[i].prosrc, "-") != 0) 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)); strcpy(func_lang, PQgetvalue(res, 0, i_lanname));
@ -3107,10 +3133,11 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
resetPQExpBuffer(q); resetPQExpBuffer(q);
appendPQExpBuffer(q, "CREATE FUNCTION %s ", fn->data ); appendPQExpBuffer(q, "CREATE FUNCTION %s ", fn->data );
appendPQExpBuffer(q, "RETURNS %s%s %s LANGUAGE '%s'", appendPQExpBuffer(q, "RETURNS %s%s %s LANGUAGE ",
(finfo[i].retset) ? " SETOF " : "", (finfo[i].retset) ? "SETOF " : "",
findTypeByOid(tinfo, numTypes, finfo[i].prorettype, zeroAsOpaque), 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 */ 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)); findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype, zeroAsOpaque + useBaseTypeName));
if (agginfo[i].agginitval) 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)) if (!(strcmp(agginfo[i].aggfinalfn, "-") == 0))
appendPQExpBuffer(details, ", FINALFUNC = %s", appendPQExpBuffer(details, ", FINALFUNC = %s",
@ -3970,7 +3999,8 @@ findLastBuiltinOid(const char* dbname)
PQExpBuffer query = createPQExpBuffer(); PQExpBuffer query = createPQExpBuffer();
resetPQExpBuffer(query); 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); res = PQexec(g_conn, query->data);
if (res == NULL || 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 static void
dumpSequence(Archive *fout, TableInfo tbinfo) dumpSequence(Archive *fout, TableInfo tbinfo)
{ {
@ -4113,8 +4108,9 @@ dumpSequence(Archive *fout, TableInfo tbinfo)
resetPQExpBuffer(query); resetPQExpBuffer(query);
appendPQExpBuffer(query, "SELECT setval ('%s', %d, '%c');\n", appendPQExpBuffer(query, "SELECT setval (");
fmtId(tbinfo.relname, force_quotes), last, called); 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, ArchiveEntry(fout, tbinfo.oid, fmtId(tbinfo.relname, force_quotes), "SEQUENCE SET", NULL,
query->data, "" /* Del */, "", "", NULL, 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, " " (select usename from pg_user where pg_class.relowner = usesysid) AS viewowner, "
" pg_rewrite.oid, pg_rewrite.rulename " " pg_rewrite.oid, pg_rewrite.rulename "
"FROM pg_rewrite, pg_class, pg_rules " "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_rewrite.ev_class = pg_class.oid "
" AND pg_rules.tablename = pg_class.relname " " AND pg_rules.tablename = pg_class.relname "
" AND pg_rules.rulename = pg_rewrite.rulename " " AND pg_rules.rulename = pg_rewrite.rulename "
"ORDER BY pg_rewrite.oid", "ORDER BY pg_rewrite.oid");
tblinfo[t].relname);
res = PQexec(g_conn, query->data); res = PQexec(g_conn, query->data);
if (!res || if (!res ||
PQresultStatus(res) != PGRES_TUPLES_OK) PQresultStatus(res) != PGRES_TUPLES_OK)