]> granicus.if.org Git - postgresql/commitdiff
Fix EXPLAIN so that it doesn't emit invalid XML in corner cases.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 20 Oct 2016 21:17:50 +0000 (17:17 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 20 Oct 2016 21:17:50 +0000 (17:17 -0400)
With track_io_timing = on, EXPLAIN (ANALYZE, BUFFERS) will emit fields
named like "I/O Read Time".  The slash makes that invalid as an XML
element name, so that adding FORMAT XML would produce invalid XML.

We already have code in there to translate spaces to dashes, so let's
generalize that to convert anything that isn't a valid XML name character,
viz letters, digits, hyphens, underscores, and periods.  We could just
reject slashes, which would run a bit faster.  But the fact that this went
unnoticed for so long doesn't give me a warm feeling that we'd notice the
next creative violation, so let's make it a permanent fix.

Reported by Markus Winand, though this isn't his initial patch proposal.

Back-patch to 9.2 where track_io_timing was added.  The problem is only
latent in 9.1, so I don't feel a need to fix it there.

Discussion: <E0BF6A45-68E8-45E6-918F-741FB332C6BB@winand.at>

src/backend/commands/explain.c

index 124743318d891cd23f1f78f044d0f2389c03f89a..0a669d9b43a8aed9e22fe40c43c4d37e2010f3af 100644 (file)
@@ -3312,13 +3312,15 @@ ExplainSeparatePlans(ExplainState *es)
  * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally
  * add.
  *
- * XML tag names can't contain white space, so we replace any spaces in
- * "tagname" with dashes.
+ * XML restricts tag names more than our other output formats, eg they can't
+ * contain white space or slashes.  Replace invalid characters with dashes,
+ * so that for example "I/O Read Time" becomes "I-O-Read-Time".
  */
 static void
 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
 {
        const char *s;
+       const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
 
        if ((flags & X_NOWHITESPACE) == 0)
                appendStringInfoSpaces(es->str, 2 * es->indent);
@@ -3326,7 +3328,7 @@ ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
        if ((flags & X_CLOSING) != 0)
                appendStringInfoCharMacro(es->str, '/');
        for (s = tagname; *s; s++)
-               appendStringInfoCharMacro(es->str, (*s == ' ') ? '-' : *s);
+               appendStringInfoChar(es->str, strchr(valid, *s) ? *s : '-');
        if ((flags & X_CLOSE_IMMEDIATE) != 0)
                appendStringInfoString(es->str, " /");
        appendStringInfoCharMacro(es->str, '>');