Skip to content

Commit 932de89

Browse files
committed
Quote all string values in EXPLAIN (FORMAT YAML) output.
While my previous attempt seems to always produce valid YAML, it doesn't always produce YAML that means what it appears to mean, because of tokens like "0xa" and "true", which without quotes will be interpreted as integer or Boolean literals. So, instead, just quote everything that's not known to be a number, as we do for JSON. Dean Rasheed, with some changes to the comments by me.
1 parent f383083 commit 932de89

File tree

1 file changed

+16
-46
lines changed

1 file changed

+16
-46
lines changed

src/backend/commands/explain.c

Lines changed: 16 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994-5, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.205 2010/06/09 02:39:34 rhaas Exp $
10+
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.206 2010/06/10 01:26:30 rhaas Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -1698,8 +1698,7 @@ ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
16981698

16991699
case EXPLAIN_FORMAT_YAML:
17001700
ExplainYAMLLineStarting(es);
1701-
escape_yaml(es->str, qlabel);
1702-
appendStringInfoChar(es->str, ':');
1701+
appendStringInfo(es->str, "%s: ", qlabel);
17031702
foreach(lc, data)
17041703
{
17051704
appendStringInfoChar(es->str, '\n');
@@ -1759,7 +1758,10 @@ ExplainProperty(const char *qlabel, const char *value, bool numeric,
17591758
case EXPLAIN_FORMAT_YAML:
17601759
ExplainYAMLLineStarting(es);
17611760
appendStringInfo(es->str, "%s: ", qlabel);
1762-
escape_yaml(es->str, value);
1761+
if (numeric)
1762+
appendStringInfoString(es->str, value);
1763+
else
1764+
escape_yaml(es->str, value);
17631765
break;
17641766
}
17651767
}
@@ -1857,8 +1859,7 @@ ExplainOpenGroup(const char *objtype, const char *labelname,
18571859
ExplainYAMLLineStarting(es);
18581860
if (labelname)
18591861
{
1860-
escape_yaml(es->str, labelname);
1861-
appendStringInfoChar(es->str, ':');
1862+
appendStringInfo(es->str, "%s: ", labelname);
18621863
es->grouping_stack = lcons_int(1, es->grouping_stack);
18631864
}
18641865
else
@@ -2152,48 +2153,17 @@ escape_json(StringInfo buf, const char *str)
21522153
}
21532154

21542155
/*
2155-
* YAML is a superset of JSON, so we can use JSON escaping when escaping is
2156-
* needed. However, some things that need to be quoted in JSON don't require
2157-
* quoting in YAML, and we prefer not to quote unnecessarily, to improve
2158-
* readability.
2159-
*
2160-
* Unfortunately, the YAML quoting rules are ridiculously complicated -- as
2161-
* documented in sections 5.3 and 7.3.3 of http://yaml.org/spec/1.2/spec.html
2162-
* -- and it doesn't seem worth expending a large amount of energy to avoid
2163-
* all unnecessary quoting, so we just do something (sort of) simple: we quote
2164-
* any string which is empty; any string which contains characters other than
2165-
* alphanumerics, period, underscore, or space; or begins or ends with a
2166-
* space. The exception for period is mostly so that floating-point numbers
2167-
* (e.g., cost values) won't be quoted.
2156+
* YAML is a superset of JSON; unfortuantely, the YAML quoting rules are
2157+
* ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
2158+
* http://yaml.org/spec/1.2/spec.html -- so we chose to just quote everything.
2159+
* Empty strings, strings with leading or trailing whitespace, and strings
2160+
* containing a variety of special characters must certainly be quoted or the
2161+
* output is invalid; and other seemingly harmless strings like "0xa" or
2162+
* "true" must be quoted, lest they be interpreted as a hexadecimal or Boolean
2163+
* constant rather than a string.
21682164
*/
21692165
static void
21702166
escape_yaml(StringInfo buf, const char *str)
21712167
{
2172-
bool needs_quoting = false;
2173-
2174-
#define is_safe_yaml(x) \
2175-
(isalnum(((unsigned char) x)) || (x) == '.' || (x) == '_')
2176-
2177-
if (!is_safe_yaml(str[0]))
2178-
needs_quoting = true;
2179-
else
2180-
{
2181-
const char *p;
2182-
2183-
for (p = str; *p; p++)
2184-
{
2185-
if (*p != ' ' && !is_safe_yaml(*p))
2186-
{
2187-
needs_quoting = true;
2188-
break;
2189-
}
2190-
}
2191-
if (!*p && p[-1] == ' ')
2192-
needs_quoting = true;
2193-
}
2194-
2195-
if (needs_quoting)
2196-
escape_json(buf, str);
2197-
else
2198-
appendStringInfoString(buf, str);
2168+
escape_json(buf, str);
21992169
}

0 commit comments

Comments
 (0)