Skip to content

Commit b01828e

Browse files
committed
Teach pg_dump to quote reloption values safely.
Commit c7e27be fixed this on the backend side, but we neglected the fact that several code paths in pg_dump were printing reloptions values that had not gotten massaged by ruleutils. Apply essentially the same quoting logic in those places, too.
1 parent 7013035 commit b01828e

File tree

2 files changed

+130
-40
lines changed

2 files changed

+130
-40
lines changed

src/bin/pg_dump/pg_dump.c

Lines changed: 128 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
251251
const char *objlabel);
252252
static const char *getAttrName(int attrnum, TableInfo *tblInfo);
253253
static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
254+
static bool nonemptyReloptions(const char *reloptions);
255+
static void fmtReloptionsArray(Archive *fout, PQExpBuffer buffer,
256+
const char *reloptions, const char *prefix);
254257
static char *get_synchronized_snapshot(Archive *fout);
255258
static PGresult *ExecuteSqlQueryForSingleRow(Archive *fout, char *query);
256259
static void setupDumpWorker(Archive *AHX, DumpOptions *dopt, RestoreOptions *ropt);
@@ -4585,10 +4588,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
45854588
"d.refobjid AS owning_tab, "
45864589
"d.refobjsubid AS owning_col, "
45874590
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
4588-
"array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
4591+
"array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
45894592
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
45904593
"WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
4591-
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
4594+
"tc.reloptions AS toast_reloptions "
45924595
"FROM pg_class c "
45934596
"LEFT JOIN pg_depend d ON "
45944597
"(c.relkind = '%c' AND "
@@ -4627,10 +4630,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
46274630
"d.refobjid AS owning_tab, "
46284631
"d.refobjsubid AS owning_col, "
46294632
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
4630-
"array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
4633+
"array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
46314634
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
46324635
"WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
4633-
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
4636+
"tc.reloptions AS toast_reloptions "
46344637
"FROM pg_class c "
46354638
"LEFT JOIN pg_depend d ON "
46364639
"(c.relkind = '%c' AND "
@@ -4669,10 +4672,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
46694672
"d.refobjid AS owning_tab, "
46704673
"d.refobjsubid AS owning_col, "
46714674
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
4672-
"array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
4675+
"array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
46734676
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
46744677
"WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
4675-
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
4678+
"tc.reloptions AS toast_reloptions "
46764679
"FROM pg_class c "
46774680
"LEFT JOIN pg_depend d ON "
46784681
"(c.relkind = '%c' AND "
@@ -4711,8 +4714,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
47114714
"d.refobjid AS owning_tab, "
47124715
"d.refobjsubid AS owning_col, "
47134716
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
4714-
"array_to_string(c.reloptions, ', ') AS reloptions, "
4715-
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
4717+
"c.reloptions AS reloptions, "
4718+
"tc.reloptions AS toast_reloptions "
47164719
"FROM pg_class c "
47174720
"LEFT JOIN pg_depend d ON "
47184721
"(c.relkind = '%c' AND "
@@ -4751,8 +4754,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
47514754
"d.refobjid AS owning_tab, "
47524755
"d.refobjsubid AS owning_col, "
47534756
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
4754-
"array_to_string(c.reloptions, ', ') AS reloptions, "
4755-
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
4757+
"c.reloptions AS reloptions, "
4758+
"tc.reloptions AS toast_reloptions "
47564759
"FROM pg_class c "
47574760
"LEFT JOIN pg_depend d ON "
47584761
"(c.relkind = '%c' AND "
@@ -4790,8 +4793,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
47904793
"d.refobjid AS owning_tab, "
47914794
"d.refobjsubid AS owning_col, "
47924795
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
4793-
"array_to_string(c.reloptions, ', ') AS reloptions, "
4794-
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
4796+
"c.reloptions AS reloptions, "
4797+
"tc.reloptions AS toast_reloptions "
47954798
"FROM pg_class c "
47964799
"LEFT JOIN pg_depend d ON "
47974800
"(c.relkind = '%c' AND "
@@ -4829,7 +4832,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
48294832
"d.refobjid AS owning_tab, "
48304833
"d.refobjsubid AS owning_col, "
48314834
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
4832-
"array_to_string(c.reloptions, ', ') AS reloptions, "
4835+
"c.reloptions AS reloptions, "
48334836
"NULL AS toast_reloptions "
48344837
"FROM pg_class c "
48354838
"LEFT JOIN pg_depend d ON "
@@ -5302,7 +5305,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
53025305
i_conoid,
53035306
i_condef,
53045307
i_tablespace,
5305-
i_options,
5308+
i_indreloptions,
53065309
i_relpages;
53075310
int ntups;
53085311

@@ -5360,7 +5363,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
53605363
"c.oid AS conoid, "
53615364
"pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
53625365
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
5363-
"array_to_string(t.reloptions, ', ') AS options "
5366+
"t.reloptions AS indreloptions "
53645367
"FROM pg_catalog.pg_index i "
53655368
"JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
53665369
"LEFT JOIN pg_catalog.pg_constraint c "
@@ -5391,7 +5394,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
53915394
"c.oid AS conoid, "
53925395
"pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
53935396
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
5394-
"array_to_string(t.reloptions, ', ') AS options "
5397+
"t.reloptions AS indreloptions "
53955398
"FROM pg_catalog.pg_index i "
53965399
"JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
53975400
"LEFT JOIN pg_catalog.pg_constraint c "
@@ -5418,7 +5421,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
54185421
"c.oid AS conoid, "
54195422
"null AS condef, "
54205423
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
5421-
"array_to_string(t.reloptions, ', ') AS options "
5424+
"t.reloptions AS indreloptions "
54225425
"FROM pg_catalog.pg_index i "
54235426
"JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
54245427
"LEFT JOIN pg_catalog.pg_depend d "
@@ -5448,7 +5451,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
54485451
"c.oid AS conoid, "
54495452
"null AS condef, "
54505453
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
5451-
"null AS options "
5454+
"null AS indreloptions "
54525455
"FROM pg_catalog.pg_index i "
54535456
"JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
54545457
"LEFT JOIN pg_catalog.pg_depend d "
@@ -5477,7 +5480,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
54775480
"c.oid AS conoid, "
54785481
"null AS condef, "
54795482
"NULL AS tablespace, "
5480-
"null AS options "
5483+
"null AS indreloptions "
54815484
"FROM pg_catalog.pg_index i "
54825485
"JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
54835486
"LEFT JOIN pg_catalog.pg_depend d "
@@ -5509,7 +5512,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
55095512
"t.oid AS conoid, "
55105513
"null AS condef, "
55115514
"NULL AS tablespace, "
5512-
"null AS options "
5515+
"null AS indreloptions "
55135516
"FROM pg_index i, pg_class t "
55145517
"WHERE t.oid = i.indexrelid "
55155518
"AND i.indrelid = '%u'::oid "
@@ -5536,7 +5539,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
55365539
"t.oid AS conoid, "
55375540
"null AS condef, "
55385541
"NULL AS tablespace, "
5539-
"null AS options "
5542+
"null AS indreloptions "
55405543
"FROM pg_index i, pg_class t "
55415544
"WHERE t.oid = i.indexrelid "
55425545
"AND i.indrelid = '%u'::oid "
@@ -5565,7 +5568,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
55655568
i_conoid = PQfnumber(res, "conoid");
55665569
i_condef = PQfnumber(res, "condef");
55675570
i_tablespace = PQfnumber(res, "tablespace");
5568-
i_options = PQfnumber(res, "options");
5571+
i_indreloptions = PQfnumber(res, "indreloptions");
55695572

55705573
indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
55715574
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
@@ -5584,7 +5587,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
55845587
indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
55855588
indxinfo[j].indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
55865589
indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
5587-
indxinfo[j].options = pg_strdup(PQgetvalue(res, j, i_options));
5590+
indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
55885591

55895592
/*
55905593
* In pre-7.4 releases, indkeys may contain more entries than
@@ -13810,8 +13813,12 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
1381013813
tbinfo->dobj.catId.oid, false);
1381113814

1381213815
appendPQExpBuffer(q, "CREATE VIEW %s", fmtId(tbinfo->dobj.name));
13813-
if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0)
13814-
appendPQExpBuffer(q, " WITH (%s)", tbinfo->reloptions);
13816+
if (nonemptyReloptions(tbinfo->reloptions))
13817+
{
13818+
appendPQExpBufferStr(q, " WITH (");
13819+
fmtReloptionsArray(fout, q, tbinfo->reloptions, "");
13820+
appendPQExpBufferChar(q, ')');
13821+
}
1381513822
result = createViewAsClause(fout, tbinfo);
1381613823
appendPQExpBuffer(q, " AS\n%s", result->data);
1381713824
destroyPQExpBuffer(result);
@@ -14055,21 +14062,22 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
1405514062
appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
1405614063
}
1405714064

14058-
if ((tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) ||
14059-
(tbinfo->toast_reloptions && strlen(tbinfo->toast_reloptions) > 0))
14065+
if (nonemptyReloptions(tbinfo->reloptions) ||
14066+
nonemptyReloptions(tbinfo->toast_reloptions))
1406014067
{
1406114068
bool addcomma = false;
1406214069

1406314070
appendPQExpBufferStr(q, "\nWITH (");
14064-
if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0)
14071+
if (nonemptyReloptions(tbinfo->reloptions))
1406514072
{
1406614073
addcomma = true;
14067-
appendPQExpBufferStr(q, tbinfo->reloptions);
14074+
fmtReloptionsArray(fout, q, tbinfo->reloptions, "");
1406814075
}
14069-
if (tbinfo->toast_reloptions && strlen(tbinfo->toast_reloptions) > 0)
14076+
if (nonemptyReloptions(tbinfo->toast_reloptions))
1407014077
{
14071-
appendPQExpBuffer(q, "%s%s", addcomma ? ", " : "",
14072-
tbinfo->toast_reloptions);
14078+
if (addcomma)
14079+
appendPQExpBufferStr(q, ", ");
14080+
fmtReloptionsArray(fout, q, tbinfo->toast_reloptions, "toast.");
1407314081
}
1407414082
appendPQExpBufferChar(q, ')');
1407514083
}
@@ -14651,8 +14659,12 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
1465114659

1465214660
appendPQExpBufferChar(q, ')');
1465314661

14654-
if (indxinfo->options && strlen(indxinfo->options) > 0)
14655-
appendPQExpBuffer(q, " WITH (%s)", indxinfo->options);
14662+
if (nonemptyReloptions(indxinfo->indreloptions))
14663+
{
14664+
appendPQExpBufferStr(q, " WITH (");
14665+
fmtReloptionsArray(fout, q, indxinfo->indreloptions, "");
14666+
appendPQExpBufferChar(q, ')');
14667+
}
1465614668

1465714669
if (coninfo->condeferrable)
1465814670
{
@@ -15512,11 +15524,12 @@ dumpRule(Archive *fout, DumpOptions *dopt, RuleInfo *rinfo)
1551215524
/*
1551315525
* Apply view's reloptions when its ON SELECT rule is separate.
1551415526
*/
15515-
if (rinfo->reloptions && strlen(rinfo->reloptions) > 0)
15527+
if (nonemptyReloptions(rinfo->reloptions))
1551615528
{
15517-
appendPQExpBuffer(cmd, "ALTER VIEW %s SET (%s);\n",
15518-
fmtId(tbinfo->dobj.name),
15519-
rinfo->reloptions);
15529+
appendPQExpBuffer(cmd, "ALTER VIEW %s SET (",
15530+
fmtId(tbinfo->dobj.name));
15531+
fmtReloptionsArray(fout, cmd, rinfo->reloptions, "");
15532+
appendPQExpBufferStr(cmd, ");\n");
1552015533
}
1552115534

1552215535
/*
@@ -16389,6 +16402,83 @@ fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
1638916402
return buffer->data;
1639016403
}
1639116404

16405+
/*
16406+
* Check if a reloptions array is nonempty.
16407+
*/
16408+
static bool
16409+
nonemptyReloptions(const char *reloptions)
16410+
{
16411+
/* Don't want to print it if it's just "{}" */
16412+
return (reloptions != NULL && strlen(reloptions) > 2);
16413+
}
16414+
16415+
/*
16416+
* Format a reloptions array and append it to the given buffer.
16417+
*
16418+
* "prefix" is prepended to the option names; typically it's "" or "toast.".
16419+
*
16420+
* Note: this logic should generally match the backend's flatten_reloptions()
16421+
* (in adt/ruleutils.c).
16422+
*/
16423+
static void
16424+
fmtReloptionsArray(Archive *fout, PQExpBuffer buffer, const char *reloptions,
16425+
const char *prefix)
16426+
{
16427+
char **options;
16428+
int noptions;
16429+
int i;
16430+
16431+
if (!parsePGArray(reloptions, &options, &noptions))
16432+
{
16433+
write_msg(NULL, "WARNING: could not parse reloptions array\n");
16434+
if (options)
16435+
free(options);
16436+
return;
16437+
}
16438+
16439+
for (i = 0; i < noptions; i++)
16440+
{
16441+
char *option = options[i];
16442+
char *name;
16443+
char *separator;
16444+
char *value;
16445+
16446+
/*
16447+
* Each array element should have the form name=value. If the "=" is
16448+
* missing for some reason, treat it like an empty value.
16449+
*/
16450+
name = option;
16451+
separator = strchr(option, '=');
16452+
if (separator)
16453+
{
16454+
*separator = '\0';
16455+
value = separator + 1;
16456+
}
16457+
else
16458+
value = "";
16459+
16460+
if (i > 0)
16461+
appendPQExpBufferStr(buffer, ", ");
16462+
appendPQExpBuffer(buffer, "%s%s=", prefix, fmtId(name));
16463+
16464+
/*
16465+
* In general we need to quote the value; but to avoid unnecessary
16466+
* clutter, do not quote if it is an identifier that would not need
16467+
* quoting. (We could also allow numbers, but that is a bit trickier
16468+
* than it looks --- for example, are leading zeroes significant? We
16469+
* don't want to assume very much here about what custom reloptions
16470+
* might mean.)
16471+
*/
16472+
if (strcmp(fmtId(value), value) == 0)
16473+
appendPQExpBufferStr(buffer, value);
16474+
else
16475+
appendStringLiteralAH(buffer, value, fout);
16476+
}
16477+
16478+
if (options)
16479+
free(options);
16480+
}
16481+
1639216482
/*
1639316483
* Execute an SQL query and verify that we got exactly one row back.
1639416484
*/

src/bin/pg_dump/pg_dump.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ typedef struct _tableInfo
205205
char relreplident; /* replica identifier */
206206
char *reltablespace; /* relation tablespace */
207207
char *reloptions; /* options specified by WITH (...) */
208-
char *checkoption; /* WITH CHECK OPTION */
208+
char *checkoption; /* WITH CHECK OPTION, if any */
209209
char *toast_reloptions; /* WITH options for the TOAST table */
210210
bool hasindex; /* does it have any indexes? */
211211
bool hasrules; /* does it have any rules? */
@@ -282,7 +282,7 @@ typedef struct _indxInfo
282282
TableInfo *indextable; /* link to table the index is for */
283283
char *indexdef;
284284
char *tablespace; /* tablespace in which index is stored */
285-
char *options; /* options specified by WITH (...) */
285+
char *indreloptions; /* options specified by WITH (...) */
286286
int indnkeys;
287287
Oid *indkeys;
288288
bool indisclustered;

0 commit comments

Comments
 (0)