Add YAML option to explain
Attached patch adds YAML output option to explain:
explain (format YAML) select * from information_schema.columns;
--
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200908281414
http://biglumber.com/x/web?pk=2529DF6AB8F79407E94445B4BC9B906714964AC8
Attachments:
yaml.explain.patchtext/x-patch; name=yaml.explain.patchDownload
Index: contrib/auto_explain/auto_explain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/auto_explain/auto_explain.c,v
retrieving revision 1.7
diff -u -r1.7 auto_explain.c
--- contrib/auto_explain/auto_explain.c 10 Aug 2009 05:46:49 -0000 1.7
+++ contrib/auto_explain/auto_explain.c 28 Aug 2009 18:12:49 -0000
@@ -29,6 +29,7 @@
{"text", EXPLAIN_FORMAT_TEXT, false},
{"xml", EXPLAIN_FORMAT_XML, false},
{"json", EXPLAIN_FORMAT_JSON, false},
+ {"yaml", EXPLAIN_FORMAT_YAML, false},
{NULL, 0, false}
};
Index: doc/src/sgml/auto-explain.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/auto-explain.sgml,v
retrieving revision 1.4
diff -u -r1.4 auto-explain.sgml
--- doc/src/sgml/auto-explain.sgml 10 Aug 2009 05:46:50 -0000 1.4
+++ doc/src/sgml/auto-explain.sgml 28 Aug 2009 18:12:49 -0000
@@ -114,7 +114,7 @@
<varname>auto_explain.log_format</varname> selects the
<command>EXPLAIN</> output format to be used.
The allowed values are <literal>text</literal>, <literal>xml</literal>,
- and <literal>json</literal>. The default is text.
+ <literal>json</literal>, and <literal>yaml</literal>. The default is text.
Only superusers can change this setting.
</para>
</listitem>
Index: doc/src/sgml/release-8.5.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/release-8.5.sgml,v
retrieving revision 1.4
diff -u -r1.4 release-8.5.sgml
--- doc/src/sgml/release-8.5.sgml 19 Aug 2009 08:18:48 -0000 1.4
+++ doc/src/sgml/release-8.5.sgml 28 Aug 2009 18:12:49 -0000
@@ -96,7 +96,7 @@
<itemizedlist>
<listitem>
<para>
- EXPLAIN allows output of plans in XML or JSON format for automated
+ EXPLAIN allows output of plans in XML, JSON, or YAML format for automated
processing of explain plans by analysis or visualization tools.
</para>
</listitem>
Index: doc/src/sgml/ref/explain.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v
retrieving revision 1.46
diff -u -r1.46 explain.sgml
--- doc/src/sgml/ref/explain.sgml 10 Aug 2009 05:46:50 -0000 1.46
+++ doc/src/sgml/ref/explain.sgml 28 Aug 2009 18:12:49 -0000
@@ -31,7 +31,7 @@
<refsynopsisdiv>
<synopsis>
-EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> | FORMAT { TEXT | XML | JSON } } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
+EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> | FORMAT { TEXT | XML | JSON | YAML } } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
</synopsis>
</refsynopsisdiv>
@@ -143,8 +143,8 @@
<term><literal>FORMAT</literal></term>
<listitem>
<para>
- Specify the output format, which can be TEXT, XML, or JSON.
- XML or JSON output contains the same information as the text output
+ Specify the output format, which can be TEXT, XML, JSON, or YAML.
+ Non-text output contains the same information as the text output
format, but is easier for programs to parse. This parameter defaults to
<literal>TEXT</literal>.
</para>
Index: src/backend/commands/explain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.190
diff -u -r1.190 explain.c
--- src/backend/commands/explain.c 22 Aug 2009 02:06:32 -0000 1.190
+++ src/backend/commands/explain.c 28 Aug 2009 18:12:49 -0000
@@ -96,6 +96,7 @@
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
static void ExplainJSONLineEnding(ExplainState *es);
static void escape_json(StringInfo buf, const char *str);
+static void escape_yaml(StringInfo buf, const char *str);
/*
@@ -137,6 +138,8 @@
es.format = EXPLAIN_FORMAT_XML;
else if (strcmp(p, "json") == 0)
es.format = EXPLAIN_FORMAT_JSON;
+ else if (strcmp(p, "yaml") == 0)
+ es.format = EXPLAIN_FORMAT_YAML;
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1509,6 +1512,18 @@
}
appendStringInfoChar(es->str, ']');
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, es->indent * 2);
+ appendStringInfo(es->str, "%s:\n", qlabel);
+ foreach(lc, data)
+ {
+ appendStringInfoSpaces(es->str, es->indent * 2 + 2);
+ appendStringInfoString(es->str, "- ");
+ escape_yaml(es->str, qlabel);
+ appendStringInfoChar(es->str, '\n');
+ }
+ break;
}
}
@@ -1556,6 +1571,17 @@
else
escape_json(es->str, value);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, es->indent * 2);
+ appendStringInfo(es->str, "%s: ", qlabel);
+ if (numeric)
+ appendStringInfoString(es->str, value);
+ else
+ escape_yaml(es->str, value);
+ appendStringInfoChar(es->str, '\n');
+ break;
+
}
}
@@ -1640,6 +1666,19 @@
es->grouping_stack = lcons_int(0, es->grouping_stack);
es->indent++;
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, 2 * es->indent);
+ if (labelname)
+ {
+ appendStringInfo(es->str, "%s:\n", labelname);
+ }
+ else {
+ appendStringInfoString(es->str, "-\n");
+ }
+ es->indent++;
+ break;
+
}
}
@@ -1669,6 +1708,10 @@
appendStringInfoChar(es->str, labeled ? '}' : ']');
es->grouping_stack = list_delete_first(es->grouping_stack);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ es->indent--;
+ break;
}
}
@@ -1693,7 +1736,6 @@
case EXPLAIN_FORMAT_JSON:
ExplainJSONLineEnding(es);
- appendStringInfoSpaces(es->str, 2 * es->indent);
if (labelname)
{
escape_json(es->str, labelname);
@@ -1701,6 +1743,15 @@
}
escape_json(es->str, objtype);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, 2 * es->indent);
+ if (labelname)
+ {
+ appendStringInfo(es->str, "%s:", labelname);
+ }
+ appendStringInfo(es->str, "%s\n", objtype);
+ break;
}
}
@@ -1716,6 +1767,7 @@
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
+ case EXPLAIN_FORMAT_YAML:
/* nothing to do */
break;
@@ -1756,6 +1808,10 @@
appendStringInfoString(es->str, "\n]");
es->grouping_stack = list_delete_first(es->grouping_stack);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ es->indent--;
+ break;
}
}
@@ -1773,6 +1829,7 @@
break;
case EXPLAIN_FORMAT_XML:
+ case EXPLAIN_FORMAT_YAML:
/* nothing to do */
break;
@@ -1874,3 +1931,31 @@
}
appendStringInfoCharMacro(buf, '\"');
}
+
+/*
+ * YAML is a superset of JSON: if we find quotable characters, we call escape_json
+ */
+static void
+escape_yaml(StringInfo buf, const char *str)
+{
+ const char *p;
+
+ for (p = str; *p; p++)
+ {
+ if ('"' == *p
+ || '\\' == *p
+ || '\t' == *p
+ || '\n' == *p
+ || '\r' == *p
+ || '\b' == *p
+ || '\f' == *p
+ || (unsigned char) *p < ' '
+ )
+ {
+ escape_json(buf, str);
+ return;
+ }
+ }
+
+ appendStringInfo(buf, "%s", str);
+}
Index: src/include/commands/explain.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/commands/explain.h,v
retrieving revision 1.41
diff -u -r1.41 explain.h
--- src/include/commands/explain.h 10 Aug 2009 05:46:50 -0000 1.41
+++ src/include/commands/explain.h 28 Aug 2009 18:12:50 -0000
@@ -19,7 +19,8 @@
{
EXPLAIN_FORMAT_TEXT,
EXPLAIN_FORMAT_XML,
- EXPLAIN_FORMAT_JSON
+ EXPLAIN_FORMAT_JSON,
+ EXPLAIN_FORMAT_YAML
} ExplainFormat;
typedef struct ExplainState
Greg Sabino Mullane wrote:
Attached patch adds YAML output option to explain:
I thought the consensus was that we didn't want to get into supporting
more formats. What does YAML provide that JSON does not?
cheers
andrew
-----BEGIN PGP SIGNED MESSAGE-----
Hash: RIPEMD160
I thought the consensus was that we didn't want to get into supporting
more formats. What does YAML provide that JSON does not?
Readability and easy editing. All the power of JSON without the
annoying quotes, braces, and brackets.
By the way, Magnus pointed out an error in the patch: the hunk at
- -1693,7 +1736,6 should be ignored.
- --
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200908281552
http://biglumber.com/x/web?pk=2529DF6AB8F79407E94445B4BC9B906714964AC8
-----BEGIN PGP SIGNATURE-----
iEYEAREDAAYFAkqYOmUACgkQvJuQZxSWSsjMogCfVV8hj0QkRQFmOq2nkUV+1bqM
QDcAoKAlc6lpuV9jbeIL8ms9HIfhLF2W
=nb+s
-----END PGP SIGNATURE-----
* Greg Sabino Mullane:
I thought the consensus was that we didn't want to get into supporting
more formats. What does YAML provide that JSON does not?Readability and easy editing. All the power of JSON without the
annoying quotes, braces, and brackets.
But YAML is much more difficult to parse than JSON. Anybody who can
afford a YAML parser can also afford a JSON parser, it is miniscule in
comparison. 8-)
On 8/28/09 1:13 PM, Greg Sabino Mullane wrote:
I thought the consensus was that we didn't want to get into supporting
more formats. What does YAML provide that JSON does not?Readability and easy editing. All the power of JSON without the
annoying quotes, braces, and brackets.
How many lines of code does YAML support add to the codebase?
While I personally like YAML, it's not like it has broad industry
support. And people wouldn't interface with the XML or JSON directly;
they'd use a library for that. That's the whole point of having those
outputs.
--
Josh Berkus
PostgreSQL Experts Inc.
www.pgexperts.com
On Fri, 2009-08-28 at 14:23 -0700, Josh Berkus wrote:
On 8/28/09 1:13 PM, Greg Sabino Mullane wrote:
I thought the consensus was that we didn't want to get into supporting
more formats. What does YAML provide that JSON does not?Readability and easy editing. All the power of JSON without the
annoying quotes, braces, and brackets.How many lines of code does YAML support add to the codebase?
While I personally like YAML, it's not like it has broad industry
support. And people wouldn't interface with the XML or JSON directly;
they'd use a library for that. That's the whole point of having those
outputs.
I am not keen on having YAML support.
Joshua D. Drake
--
PostgreSQL.org Major Contributor
Command Prompt, Inc: http://www.commandprompt.com/ - 503.667.4564
Consulting, Training, Support, Custom Development, Engineering
On fre, 2009-08-28 at 20:13 +0000, Greg Sabino Mullane wrote:
Readability and easy editing. All the power of JSON without the
annoying quotes, braces, and brackets.
But these are supposed to be machine-readable formats. So readability
and editability are not high priority criteria.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: RIPEMD160
How many lines of code does YAML support add to the codebase?
About 80.
While I personally like YAML, it's not like it has broad industry
support. And people wouldn't interface with the XML or JSON directly;
they'd use a library for that. That's the whole point of having those
outputs.
Not sure how one measures "broad industry support" or why we would care
that much. Nor do I wish to turn this thread into a YAML flamewar.
It's a small patch that should be useful to the many people that
prefer to program using YAML.
- --
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200908281741
http://biglumber.com/x/web?pk=2529DF6AB8F79407E94445B4BC9B906714964AC8
-----BEGIN PGP SIGNATURE-----
iEYEAREDAAYFAkqYT0QACgkQvJuQZxSWSshPMACg+ho+gTxeKR27pOJrbX7kTJ9E
kZIAnRIz57KzuLAAr6PtjupBwletuXRe
=ZJO7
-----END PGP SIGNATURE-----
* Greg Sabino Mullane (greg@turnstep.com) wrote:
Attached patch adds YAML output option to explain:
explain (format YAML) select * from information_schema.columns;
+1 from me. I've read the other comments and just plain don't agree
with them. It's a small patch, adds a useful format for EXPLAIN, and
would be used.
One of the best things about PG is the flexibility and usability.
Thanks,
Stephen
On Aug 28, 2009, at 3:45 PM, Stephen Frost wrote:
+1 from me. I've read the other comments and just plain don't agree
with them. It's a small patch, adds a useful format for EXPLAIN, and
would be used.One of the best things about PG is the flexibility and usability.
I agree, I tend to prefer YAML output where it's parseable (and I
expect it the EXPLAIN YAML output won't be doing anything tricky).
That said, maybe there should be a way to create modules add formats,
instead of adding them to core?
Best,
David
Peter Eisentraut wrote:
On fre, 2009-08-28 at 20:13 +0000, Greg Sabino Mullane wrote:
Readability and easy editing. All the power of JSON without the
annoying quotes, braces, and brackets.But these are supposed to be machine-readable formats. So readability
and editability are not high priority criteria.
Greg, can we see a few examples of the YAML output compared to
both json and text?
IMVHO, an advantage of YAML is human readability of structured
data even compared to most non-computer-parseable human-intended
text formats. But maybe that's just because I read too much yaml.
On Fri, Aug 28, 2009 at 04:37:41PM -0700, David E. Wheeler wrote:
On Aug 28, 2009, at 3:45 PM, Stephen Frost wrote:
+1 from me. I've read the other comments and just plain don't agree
with them. It's a small patch, adds a useful format for EXPLAIN, and
would be used.One of the best things about PG is the flexibility and usability.
I agree, I tend to prefer YAML output where it's parseable (and I
expect it the EXPLAIN YAML output won't be doing anything tricky).That said, maybe there should be a way to create modules add formats,
instead of adding them to core?
+1
-dg
--
David Gould daveg@sonic.net 510 536 1443 510 282 0869
If simplicity worked, the world would be overrun with insects.
On 08/28/2009 02:16 PM, Greg Sabino Mullane wrote:
Attached patch adds YAML output option to explain:
explain (format YAML) select * from information_schema.columns;
Updated version of the patch attached, fixes two small errors.
--
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200908310847
http://biglumber.com/x/web?pk=2529DF6AB8F79407E94445B4BC9B906714964AC8
Attachments:
yaml.explain.v2.patchtext/x-patch; name=yaml.explain.v2.patchDownload
Index: contrib/auto_explain/auto_explain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/auto_explain/auto_explain.c,v
retrieving revision 1.7
diff -c -r1.7 auto_explain.c
*** contrib/auto_explain/auto_explain.c 10 Aug 2009 05:46:49 -0000 1.7
--- contrib/auto_explain/auto_explain.c 31 Aug 2009 13:36:41 -0000
***************
*** 29,34 ****
--- 29,35 ----
{"text", EXPLAIN_FORMAT_TEXT, false},
{"xml", EXPLAIN_FORMAT_XML, false},
{"json", EXPLAIN_FORMAT_JSON, false},
+ {"yaml", EXPLAIN_FORMAT_YAML, false},
{NULL, 0, false}
};
Index: doc/src/sgml/auto-explain.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/auto-explain.sgml,v
retrieving revision 1.4
diff -c -r1.4 auto-explain.sgml
*** doc/src/sgml/auto-explain.sgml 10 Aug 2009 05:46:50 -0000 1.4
--- doc/src/sgml/auto-explain.sgml 31 Aug 2009 13:36:41 -0000
***************
*** 114,120 ****
<varname>auto_explain.log_format</varname> selects the
<command>EXPLAIN</> output format to be used.
The allowed values are <literal>text</literal>, <literal>xml</literal>,
! and <literal>json</literal>. The default is text.
Only superusers can change this setting.
</para>
</listitem>
--- 114,120 ----
<varname>auto_explain.log_format</varname> selects the
<command>EXPLAIN</> output format to be used.
The allowed values are <literal>text</literal>, <literal>xml</literal>,
! <literal>json</literal>, and <literal>yaml</literal>. The default is text.
Only superusers can change this setting.
</para>
</listitem>
Index: doc/src/sgml/release-8.5.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/release-8.5.sgml,v
retrieving revision 1.4
diff -c -r1.4 release-8.5.sgml
*** doc/src/sgml/release-8.5.sgml 19 Aug 2009 08:18:48 -0000 1.4
--- doc/src/sgml/release-8.5.sgml 31 Aug 2009 13:36:41 -0000
***************
*** 96,102 ****
<itemizedlist>
<listitem>
<para>
! EXPLAIN allows output of plans in XML or JSON format for automated
processing of explain plans by analysis or visualization tools.
</para>
</listitem>
--- 96,102 ----
<itemizedlist>
<listitem>
<para>
! EXPLAIN allows output of plans in XML, JSON, or YAML format for automated
processing of explain plans by analysis or visualization tools.
</para>
</listitem>
Index: doc/src/sgml/ref/explain.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v
retrieving revision 1.46
diff -c -r1.46 explain.sgml
*** doc/src/sgml/ref/explain.sgml 10 Aug 2009 05:46:50 -0000 1.46
--- doc/src/sgml/ref/explain.sgml 31 Aug 2009 13:36:41 -0000
***************
*** 31,37 ****
<refsynopsisdiv>
<synopsis>
! EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> | FORMAT { TEXT | XML | JSON } } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
</synopsis>
</refsynopsisdiv>
--- 31,37 ----
<refsynopsisdiv>
<synopsis>
! EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> | FORMAT { TEXT | XML | JSON | YAML } } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
</synopsis>
</refsynopsisdiv>
***************
*** 143,150 ****
<term><literal>FORMAT</literal></term>
<listitem>
<para>
! Specify the output format, which can be TEXT, XML, or JSON.
! XML or JSON output contains the same information as the text output
format, but is easier for programs to parse. This parameter defaults to
<literal>TEXT</literal>.
</para>
--- 143,150 ----
<term><literal>FORMAT</literal></term>
<listitem>
<para>
! Specify the output format, which can be TEXT, XML, JSON, or YAML.
! Non-text output contains the same information as the text output
format, but is easier for programs to parse. This parameter defaults to
<literal>TEXT</literal>.
</para>
Index: src/backend/commands/explain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.190
diff -c -r1.190 explain.c
*** src/backend/commands/explain.c 22 Aug 2009 02:06:32 -0000 1.190
--- src/backend/commands/explain.c 31 Aug 2009 13:36:41 -0000
***************
*** 96,101 ****
--- 96,102 ----
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
static void ExplainJSONLineEnding(ExplainState *es);
static void escape_json(StringInfo buf, const char *str);
+ static void escape_yaml(StringInfo buf, const char *str);
/*
***************
*** 137,142 ****
--- 138,145 ----
es.format = EXPLAIN_FORMAT_XML;
else if (strcmp(p, "json") == 0)
es.format = EXPLAIN_FORMAT_JSON;
+ else if (strcmp(p, "yaml") == 0)
+ es.format = EXPLAIN_FORMAT_YAML;
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
***************
*** 1509,1514 ****
--- 1512,1529 ----
}
appendStringInfoChar(es->str, ']');
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, es->indent * 2);
+ appendStringInfo(es->str, "%s:\n", qlabel);
+ foreach(lc, data)
+ {
+ appendStringInfoSpaces(es->str, es->indent * 2 + 2);
+ appendStringInfoString(es->str, "- ");
+ escape_yaml(es->str, (const char *) lfirst(lc));
+ appendStringInfoChar(es->str, '\n');
+ }
+ break;
}
}
***************
*** 1556,1561 ****
--- 1571,1587 ----
else
escape_json(es->str, value);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, es->indent * 2);
+ appendStringInfo(es->str, "%s: ", qlabel);
+ if (numeric)
+ appendStringInfoString(es->str, value);
+ else
+ escape_yaml(es->str, value);
+ appendStringInfoChar(es->str, '\n');
+ break;
+
}
}
***************
*** 1640,1645 ****
--- 1666,1684 ----
es->grouping_stack = lcons_int(0, es->grouping_stack);
es->indent++;
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, 2 * es->indent);
+ if (labelname)
+ {
+ appendStringInfo(es->str, "%s:\n", labelname);
+ }
+ else {
+ appendStringInfoString(es->str, "-\n");
+ }
+ es->indent++;
+ break;
+
}
}
***************
*** 1669,1674 ****
--- 1708,1717 ----
appendStringInfoChar(es->str, labeled ? '}' : ']');
es->grouping_stack = list_delete_first(es->grouping_stack);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ es->indent--;
+ break;
}
}
***************
*** 1701,1706 ****
--- 1744,1758 ----
}
escape_json(es->str, objtype);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, 2 * es->indent);
+ if (labelname)
+ {
+ appendStringInfo(es->str, "%s:", labelname);
+ }
+ appendStringInfo(es->str, "%s\n", objtype);
+ break;
}
}
***************
*** 1716,1721 ****
--- 1768,1774 ----
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
+ case EXPLAIN_FORMAT_YAML:
/* nothing to do */
break;
***************
*** 1756,1761 ****
--- 1809,1818 ----
appendStringInfoString(es->str, "\n]");
es->grouping_stack = list_delete_first(es->grouping_stack);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ es->indent--;
+ break;
}
}
***************
*** 1773,1778 ****
--- 1830,1836 ----
break;
case EXPLAIN_FORMAT_XML:
+ case EXPLAIN_FORMAT_YAML:
/* nothing to do */
break;
***************
*** 1874,1876 ****
--- 1932,1962 ----
}
appendStringInfoCharMacro(buf, '\"');
}
+
+ /*
+ * YAML is a superset of JSON: if we find quotable characters, we call escape_json
+ */
+ static void
+ escape_yaml(StringInfo buf, const char *str)
+ {
+ const char *p;
+
+ for (p = str; *p; p++)
+ {
+ if ('"' == *p
+ || '\\' == *p
+ || '\t' == *p
+ || '\n' == *p
+ || '\r' == *p
+ || '\b' == *p
+ || '\f' == *p
+ || (unsigned char) *p < ' '
+ )
+ {
+ escape_json(buf, str);
+ return;
+ }
+ }
+
+ appendStringInfo(buf, "%s", str);
+ }
Index: src/include/commands/explain.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/commands/explain.h,v
retrieving revision 1.41
diff -c -r1.41 explain.h
*** src/include/commands/explain.h 10 Aug 2009 05:46:50 -0000 1.41
--- src/include/commands/explain.h 31 Aug 2009 13:36:41 -0000
***************
*** 19,25 ****
{
EXPLAIN_FORMAT_TEXT,
EXPLAIN_FORMAT_XML,
! EXPLAIN_FORMAT_JSON
} ExplainFormat;
typedef struct ExplainState
--- 19,26 ----
{
EXPLAIN_FORMAT_TEXT,
EXPLAIN_FORMAT_XML,
! EXPLAIN_FORMAT_JSON,
! EXPLAIN_FORMAT_YAML
} ExplainFormat;
typedef struct ExplainState
-----BEGIN PGP SIGNED MESSAGE-----
Hash: RIPEMD160
Greg, can we see a few examples of the YAML output
compared to both json and text?
Sure. Be warned it will make this email long. Because email may wrap things
funny, I'll post the same thing here:
Query 1:
http://pgsql.privatepaste.com/298pqiSwdH
Note that YAML quotes things like JSON does, but only when the quotes are needed.
Query 2:
http://pgsql.privatepaste.com/610uDDyMu6
greg=# explain (format text, analyze on) select * from pg_class where relname ~ 'x' order by 1,2,3;
QUERY PLAN
- --------------------------------------------------------------------------------------------------------------
Sort (cost=12.82..13.10 rows=111 width=185) (actual time=1.176..1.401 rows=105 loops=1)
Sort Key: relname, relnamespace, reltype
Sort Method: quicksort Memory: 44kB
-> Seq Scan on pg_class (cost=0.00..9.05 rows=111 width=185) (actual time=0.066..0.828 rows=105 loops=1)
Filter: (relname ~ 'x'::text)
Total runtime: 1.676 ms
greg=# explain (format json, analyze on) select * from pg_class where relname ~ 'x' order by 1,2,3;
QUERY PLAN
- -----------------------------------------------------------
[
{
"Plan": {
"Node Type": "Sort",
"Startup Cost": 12.82,
"Total Cost": 13.10,
"Plan Rows": 111,
"Plan Width": 185,
"Actual Startup Time": 1.152,
"Actual Total Time": 1.373,
"Actual Rows": 105,
"Actual Loops": 1,
"Sort Key": ["relname", "relnamespace", "reltype"],
"Sort Method": "quicksort",
"Sort Space Used": 44,
"Sort Space Type": "Memory",
"Plans": [
{
"Node Type": "Seq Scan",
"Parent Relationship": "Outer",
"Relation Name": "pg_class",
"Alias": "pg_class",
"Startup Cost": 0.00,
"Total Cost": 9.05,
"Plan Rows": 111,
"Plan Width": 185,
"Actual Startup Time": 0.067,
"Actual Total Time": 0.817,
"Actual Rows": 105,
"Actual Loops": 1,
"Filter": "(relname ~ 'x'::text)"
}
]
},
"Triggers": [
],
"Total Runtime": 1.649
}
]
greg=# explain (format yaml, analyze on) select * from pg_class where relname ~ 'x' order by 1,2,3;
QUERY PLAN
- ---------------------------------------
-
Plan:
Node Type: Sort
Startup Cost: 12.82
Total Cost: 13.10
Plan Rows: 111
Plan Width: 185
Actual Startup Time: 1.159
Actual Total Time: 1.391
Actual Rows: 105
Actual Loops: 1
Sort Key:
- relname
- relnamespace
- reltype
Sort Method: quicksort
Sort Space Used: 44
Sort Space Type: Memory
Plans:
-
Node Type: Seq Scan
Parent Relationship: Outer
Relation Name: pg_class
Alias: pg_class
Startup Cost: 0.00
Total Cost: 9.05
Plan Rows: 111
Plan Width: 185
Actual Startup Time: 0.067
Actual Total Time: 0.829
Actual Rows: 105
Actual Loops: 1
Filter: (relname ~ 'x'::text)
Triggers:
Total Runtime: 1.671
greg=# explain (format xml, analyze on) select * from pg_class where relname ~ 'x' order by 1,2,3;
QUERY PLAN
- ------------------------------------------------------------
<explain xmlns="http://www.postgresql.org/2009/explain">
<Query>
<Plan>
<Node-Type>Sort</Node-Type>
<Startup-Cost>12.82</Startup-Cost>
<Total-Cost>13.10</Total-Cost>
<Plan-Rows>111</Plan-Rows>
<Plan-Width>185</Plan-Width>
<Actual-Startup-Time>1.154</Actual-Startup-Time>
<Actual-Total-Time>1.382</Actual-Total-Time>
<Actual-Rows>105</Actual-Rows>
<Actual-Loops>1</Actual-Loops>
<Sort-Key>
<Item>relname</Item>
<Item>relnamespace</Item>
<Item>reltype</Item>
</Sort-Key>
<Sort-Method>quicksort</Sort-Method>
<Sort-Space-Used>44</Sort-Space-Used>
<Sort-Space-Type>Memory</Sort-Space-Type>
<Plans>
<Plan>
<Node-Type>Seq Scan</Node-Type>
<Parent-Relationship>Outer</Parent-Relationship>
<Relation-Name>pg_class</Relation-Name>
<Alias>pg_class</Alias>
<Startup-Cost>0.00</Startup-Cost>
<Total-Cost>9.05</Total-Cost>
<Plan-Rows>111</Plan-Rows>
<Plan-Width>185</Plan-Width>
<Actual-Startup-Time>0.066</Actual-Startup-Time>
<Actual-Total-Time>0.837</Actual-Total-Time>
<Actual-Rows>105</Actual-Rows>
<Actual-Loops>1</Actual-Loops>
<Filter>(relname ~ 'x'::text)</Filter>
</Plan>
</Plans>
</Plan>
<Triggers>
</Triggers>
<Total-Runtime>1.655</Total-Runtime>
</Query>
</explain>
An example with embedded quotes:
greg=# explain (format text, analyze on) select 1 from pg_class where relname = 'foo"bar"';
QUERY PLAN
- ----------------------------------------------------------------------------------------------------------------------------
Index Scan using pg_class_relname_nsp_index on pg_class (cost=0.00..8.27 rows=1 width=0) (actual time=0.018..0.018 rows=0 loops=1)
Index Cond: (relname = 'foo"bar"'::name)
Total runtime: 0.056 ms
greg=# explain (format json, analyze on) select 1 from pg_class where relname = 'foo"bar"';
QUERY PLAN
- ------------------------------------------------------
[
{
"Plan": {
"Node Type": "Index Scan",
"Scan Direction": "Forward",
"Index Name": "pg_class_relname_nsp_index",
"Relation Name": "pg_class",
"Alias": "pg_class",
"Startup Cost": 0.00,
"Total Cost": 8.27,
"Plan Rows": 1,
"Plan Width": 0,
"Actual Startup Time": 0.015,
"Actual Total Time": 0.015,
"Actual Rows": 0,
"Actual Loops": 1,
"Index Cond": "(relname = 'foo\"bar\"'::name)"
},
"Triggers": [
],
"Total Runtime": 0.046
}
]
greg=# explain (format yaml, analyze on) select 1 from pg_class where relname = 'foo"bar"';
QUERY PLAN
- --------------------------------------------------
-
Plan:
Node Type: Index Scan
Scan Direction: Forward
Index Name: pg_class_relname_nsp_index
Relation Name: pg_class
Alias: pg_class
Startup Cost: 0.00
Total Cost: 8.27
Plan Rows: 1
Plan Width: 0
Actual Startup Time: 0.019
Actual Total Time: 0.019
Actual Rows: 0
Actual Loops: 1
Index Cond: "(relname = 'foo\"bar\"'::name)"
Triggers:
Total Runtime: 0.058
greg=# explain (format xml, analyze on) select 1 from pg_class where relname = 'foo"bar"';
QUERY PLAN
- -------------------------------------------------------------
<explain xmlns="http://www.postgresql.org/2009/explain">
<Query>
<Plan>
<Node-Type>Index Scan</Node-Type>
<Scan-Direction>Forward</Scan-Direction>
<Index-Name>pg_class_relname_nsp_index</Index-Name>
<Relation-Name>pg_class</Relation-Name>
<Alias>pg_class</Alias>
<Startup-Cost>0.00</Startup-Cost>
<Total-Cost>8.27</Total-Cost>
<Plan-Rows>1</Plan-Rows>
<Plan-Width>0</Plan-Width>
<Actual-Startup-Time>0.013</Actual-Startup-Time>
<Actual-Total-Time>0.013</Actual-Total-Time>
<Actual-Rows>0</Actual-Rows>
<Actual-Loops>1</Actual-Loops>
<Index-Cond>(relname = 'foo"bar"'::name)</Index-Cond>
</Plan>
<Triggers>
</Triggers>
<Total-Runtime>0.049</Total-Runtime>
</Query>
</explain>
- --
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200908311000
http://biglumber.com/x/web?pk=2529DF6AB8F79407E94445B4BC9B906714964AC8
-----BEGIN PGP SIGNATURE-----
iEYEAREDAAYFAkqb2r0ACgkQvJuQZxSWSshbEACgzAIXc6dNM/+dDmE8Xvjyg147
SrsAniMfB5RBhnq9EWY95+fiDSkLCRPy
=G8Al
-----END PGP SIGNATURE-----
"Greg Sabino Mullane" <greg@turnstep.com> wrote:
-
Plan:
Node Type: Index Scan
Scan Direction: Forward
Index Name: pg_class_relname_nsp_index
Relation Name: pg_class
Alias: pg_class
Startup Cost: 0.00
Total Cost: 8.27
Plan Rows: 1
Plan Width: 0
Actual Startup Time: 0.019
Actual Total Time: 0.019
Actual Rows: 0
Actual Loops: 1
Index Cond: "(relname = 'foo\"bar\"'::name)"
Triggers:
Total Runtime: 0.058
+1 for including this format. On a ten point scale for human
readability, I'd give this about a nine. It's something I'd be
comfortable generating in order to annotate and include in an email to
programmers or managers who wouldn't have a clue how to read the
current text version of a plan.
-Kevin
On Mon, Aug 31, 2009 at 02:15:08PM -0000, Greg Sabino Mullane wrote:
Greg, can we see a few examples of the YAML output
compared to both json and text?
...
greg=# explain (format json, analyze on) select * from pg_class where relname ~ 'x' order by 1,2,3;
QUERY PLAN
- -----------------------------------------------------------
An interesting property of json, it is almost exactly the same as python
data structure syntax. If I paste the following into python:
plan = [
{
"Plan": {
"Node Type": "Sort",
"Startup Cost": 12.82,
"Total Cost": 13.10,
"Plan Rows": 111,
"Plan Width": 185,
"Actual Startup Time": 1.152,
"Actual Total Time": 1.373,
"Actual Rows": 105,
"Actual Loops": 1,
"Sort Key": ["relname", "relnamespace", "reltype"],
"Sort Method": "quicksort",
"Sort Space Used": 44,
"Sort Space Type": "Memory",
"Plans": [
{
"Node Type": "Seq Scan",
"Parent Relationship": "Outer",
"Relation Name": "pg_class",
"Alias": "pg_class",
"Startup Cost": 0.00,
"Total Cost": 9.05,
"Plan Rows": 111,
"Plan Width": 185,
"Actual Startup Time": 0.067,
"Actual Total Time": 0.817,
"Actual Rows": 105,
"Actual Loops": 1,
"Filter": "(relname ~ 'x'::text)"
}
]
},
"Triggers": [
],
"Total Runtime": 1.649
}
]
I get a python data structure. Which can be manipulated directly, or pretty
printed:
import pprint
pprint.pprint(plan)
[{'Plan': {'Actual Loops': 1,
'Actual Rows': 105,
'Actual Startup Time': 1.1519999999999999,
'Actual Total Time': 1.373,
'Node Type': 'Sort',
'Plan Rows': 111,
'Plan Width': 185,
'Plans': [{'Actual Loops': 1,
'Actual Rows': 105,
'Actual Startup Time': 0.067000000000000004,
'Actual Total Time': 0.81699999999999995,
'Alias': 'pg_class',
'Filter': "(relname ~ 'x'::text)",
'Node Type': 'Seq Scan',
'Parent Relationship': 'Outer',
'Plan Rows': 111,
'Plan Width': 185,
'Relation Name': 'pg_class',
'Startup Cost': 0.0,
'Total Cost': 9.0500000000000007}],
'Sort Key': ['relname', 'relnamespace', 'reltype'],
'Sort Method': 'quicksort',
'Sort Space Type': 'Memory',
'Sort Space Used': 44,
'Startup Cost': 12.82,
'Total Cost': 13.1},
'Total Runtime': 1.649,
'Triggers': []}]
I'm not sure if all json can be read this way, but the python and json
notations are very similar.
-dg
--
David Gould daveg@sonic.net 510 536 1443 510 282 0869
If simplicity worked, the world would be overrun with insects.
Greg Sabino Mullane <greg@turnstep.com> wrote:
On 08/28/2009 02:16 PM, Greg Sabino Mullane wrote:
Attached patch adds YAML output option to explain:
explain (format YAML) select * from information_schema.columns;Updated version of the patch attached, fixes two small errors.
I've reviewed your patch. I had a trouble on assertion failure.
Maybe es->indent-- in ExplainEndOutput() is wrong.
TRAP: FailedAssertion("!(es.indent == 0)", File: "explain.c", Line: 198)
LOG: server process (PID 28750) was terminated by signal 6: Aborted
Second issue is a linebreak at the first line of yaml list.
Can we remove the linebreak between '-' and 'Plan' ?
=# EXPLAIN (format yaml) SELECT * FROM pgbench_accounts;
QUERY PLAN
-------------------------------------
-
Plan:
Node Type: Seq Scan
Relation Name: pgbench_accounts
...
I tried to fix the above issues in the attached v3 patch. I also rewrite
grouping_stack field in ExplainState into a *real* stack variable using
ExplainStateStack struct.
Other changes are only for minor cleanup:
- Normalize "es->indent * 2" and "2 * es->indent".
- Adjust posisions of '{' and '}'.
- Rewrite if-expressions to strchr().
My rewrite is relatively large. Please reversely-review the patch.
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Attachments:
yaml.explain.v3.patchapplication/octet-stream; name=yaml.explain.v3.patchDownload
diff -cprN head/contrib/auto_explain/auto_explain.c work/contrib/auto_explain/auto_explain.c
*** head/contrib/auto_explain/auto_explain.c 2009-08-10 14:46:49.000000000 +0900
--- work/contrib/auto_explain/auto_explain.c 2009-11-16 10:03:57.929619402 +0900
*************** static const struct config_enum_entry fo
*** 29,34 ****
--- 29,35 ----
{"text", EXPLAIN_FORMAT_TEXT, false},
{"xml", EXPLAIN_FORMAT_XML, false},
{"json", EXPLAIN_FORMAT_JSON, false},
+ {"yaml", EXPLAIN_FORMAT_YAML, false},
{NULL, 0, false}
};
diff -cprN head/doc/src/sgml/auto-explain.sgml work/doc/src/sgml/auto-explain.sgml
*** head/doc/src/sgml/auto-explain.sgml 2009-08-10 14:46:50.000000000 +0900
--- work/doc/src/sgml/auto-explain.sgml 2009-11-16 10:03:57.929619402 +0900
*************** LOAD 'auto_explain';
*** 114,120 ****
<varname>auto_explain.log_format</varname> selects the
<command>EXPLAIN</> output format to be used.
The allowed values are <literal>text</literal>, <literal>xml</literal>,
! and <literal>json</literal>. The default is text.
Only superusers can change this setting.
</para>
</listitem>
--- 114,120 ----
<varname>auto_explain.log_format</varname> selects the
<command>EXPLAIN</> output format to be used.
The allowed values are <literal>text</literal>, <literal>xml</literal>,
! <literal>json</literal>, and <literal>yaml</literal>. The default is text.
Only superusers can change this setting.
</para>
</listitem>
diff -cprN head/doc/src/sgml/ref/explain.sgml work/doc/src/sgml/ref/explain.sgml
*** head/doc/src/sgml/ref/explain.sgml 2009-08-10 14:46:50.000000000 +0900
--- work/doc/src/sgml/ref/explain.sgml 2009-11-16 10:03:57.930424609 +0900
*************** PostgreSQL documentation
*** 31,37 ****
<refsynopsisdiv>
<synopsis>
! EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> | FORMAT { TEXT | XML | JSON } } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
</synopsis>
</refsynopsisdiv>
--- 31,37 ----
<refsynopsisdiv>
<synopsis>
! EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> | FORMAT { TEXT | XML | JSON | YAML } } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
</synopsis>
</refsynopsisdiv>
*************** ROLLBACK;
*** 143,150 ****
<term><literal>FORMAT</literal></term>
<listitem>
<para>
! Specify the output format, which can be TEXT, XML, or JSON.
! XML or JSON output contains the same information as the text output
format, but is easier for programs to parse. This parameter defaults to
<literal>TEXT</literal>.
</para>
--- 143,150 ----
<term><literal>FORMAT</literal></term>
<listitem>
<para>
! Specify the output format, which can be TEXT, XML, JSON, or YAML.
! Non-text output contains the same information as the text output
format, but is easier for programs to parse. This parameter defaults to
<literal>TEXT</literal>.
</para>
diff -cprN head/doc/src/sgml/release-8.5.sgml work/doc/src/sgml/release-8.5.sgml
*** head/doc/src/sgml/release-8.5.sgml 2009-10-22 04:43:06.000000000 +0900
--- work/doc/src/sgml/release-8.5.sgml 2009-11-16 10:03:57.930424609 +0900
***************
*** 176,182 ****
</listitem>
<listitem>
<para>
! EXPLAIN allows output of plans in XML or JSON format for automated
processing of explain plans by analysis or visualization tools.
</para>
</listitem>
--- 176,182 ----
</listitem>
<listitem>
<para>
! EXPLAIN allows output of plans in XML, JSON, or YAML format for automated
processing of explain plans by analysis or visualization tools.
</para>
</listitem>
diff -cprN head/src/backend/commands/explain.c work/src/backend/commands/explain.c
*** head/src/backend/commands/explain.c 2009-11-05 07:26:04.000000000 +0900
--- work/src/backend/commands/explain.c 2009-11-16 12:07:07.792412380 +0900
*************** ExplainOneQuery_hook_type ExplainOneQuer
*** 41,46 ****
--- 41,71 ----
/* Hook for plugins to get control in explain_get_index_name() */
explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
+ /* format-specific group data */
+ union ExplainGroup
+ {
+ struct
+ {
+ int save_indent;
+ } text;
+ struct
+ {
+ bool emitted;
+ } json;
+ struct
+ {
+ bool firstline;
+ } yaml;
+ };
+
+ typedef struct ExplainStateStack
+ {
+ const char *objtype; /* type of the group */
+ bool labeled; /* is the group labeled? */
+
+ ExplainGroup *prev; /* link to previous group */
+ ExplainGroup group; /* current group data */
+ } ExplainStateStack;
/* OR-able flags for ExplainXMLTag() */
#define X_OPENING 0
*************** static void ExplainPropertyLong(const ch
*** 86,101 ****
static void ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
ExplainState *es);
static void ExplainOpenGroup(const char *objtype, const char *labelname,
! bool labeled, ExplainState *es);
! static void ExplainCloseGroup(const char *objtype, const char *labelname,
! bool labeled, ExplainState *es);
static void ExplainDummyGroup(const char *objtype, const char *labelname,
ExplainState *es);
! static void ExplainBeginOutput(ExplainState *es);
static void ExplainEndOutput(ExplainState *es);
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
static void ExplainJSONLineEnding(ExplainState *es);
static void escape_json(StringInfo buf, const char *str);
/*
--- 111,126 ----
static void ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
ExplainState *es);
static void ExplainOpenGroup(const char *objtype, const char *labelname,
! bool labeled, ExplainState *es, ExplainStateStack *stack);
! static void ExplainCloseGroup(ExplainState *es, const ExplainStateStack *stack);
static void ExplainDummyGroup(const char *objtype, const char *labelname,
ExplainState *es);
! static void ExplainBeginOutput(ExplainState *es, ExplainStateStack *stack);
static void ExplainEndOutput(ExplainState *es);
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
static void ExplainJSONLineEnding(ExplainState *es);
static void escape_json(StringInfo buf, const char *str);
+ static void escape_yaml(StringInfo buf, const char *str);
/*
*************** ExplainQuery(ExplainStmt *stmt, const ch
*** 107,112 ****
--- 132,138 ----
ParamListInfo params, DestReceiver *dest)
{
ExplainState es;
+ ExplainStateStack stack;
TupOutputState *tstate;
List *rewritten;
ListCell *lc;
*************** ExplainQuery(ExplainStmt *stmt, const ch
*** 135,140 ****
--- 161,168 ----
es.format = EXPLAIN_FORMAT_XML;
else if (strcmp(p, "json") == 0)
es.format = EXPLAIN_FORMAT_JSON;
+ else if (strcmp(p, "yaml") == 0)
+ es.format = EXPLAIN_FORMAT_YAML;
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
*************** ExplainQuery(ExplainStmt *stmt, const ch
*** 164,170 ****
params);
/* emit opening boilerplate */
! ExplainBeginOutput(&es);
if (rewritten == NIL)
{
--- 192,198 ----
params);
/* emit opening boilerplate */
! ExplainBeginOutput(&es, &stack);
if (rewritten == NIL)
{
*************** ExplainOnePlan(PlannedStmt *plannedstmt,
*** 337,342 ****
--- 365,371 ----
instr_time starttime;
double totaltime = 0;
int eflags;
+ ExplainStateStack stack;
/*
* Use a snapshot with an updated command ID to ensure this query sees
*************** ExplainOnePlan(PlannedStmt *plannedstmt,
*** 374,380 ****
totaltime += elapsed_time(&starttime);
}
! ExplainOpenGroup("Query", NULL, true, es);
/* Create textual dump of plan tree */
ExplainPrintPlan(es, queryDesc);
--- 403,409 ----
totaltime += elapsed_time(&starttime);
}
! ExplainOpenGroup("Query", NULL, true, es, &stack);
/* Create textual dump of plan tree */
ExplainPrintPlan(es, queryDesc);
*************** ExplainOnePlan(PlannedStmt *plannedstmt,
*** 400,407 ****
List *targrels = queryDesc->estate->es_trig_target_relations;
int nr;
ListCell *l;
! ExplainOpenGroup("Triggers", "Triggers", false, es);
show_relname = (numrels > 1 || targrels != NIL);
rInfo = queryDesc->estate->es_result_relations;
--- 429,437 ----
List *targrels = queryDesc->estate->es_trig_target_relations;
int nr;
ListCell *l;
+ ExplainStateStack trig_stack;
! ExplainOpenGroup("Triggers", "Triggers", false, es, &trig_stack);
show_relname = (numrels > 1 || targrels != NIL);
rInfo = queryDesc->estate->es_result_relations;
*************** ExplainOnePlan(PlannedStmt *plannedstmt,
*** 414,420 ****
report_triggers(rInfo, show_relname, es);
}
! ExplainCloseGroup("Triggers", "Triggers", false, es);
}
/*
--- 444,450 ----
report_triggers(rInfo, show_relname, es);
}
! ExplainCloseGroup(es, &trig_stack);
}
/*
*************** ExplainOnePlan(PlannedStmt *plannedstmt,
*** 445,451 ****
3, es);
}
! ExplainCloseGroup("Query", NULL, true, es);
}
/*
--- 475,481 ----
3, es);
}
! ExplainCloseGroup(es, &stack);
}
/*
*************** report_triggers(ResultRelInfo *rInfo, bo
*** 485,490 ****
--- 515,521 ----
Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
char *relname;
char *conname = NULL;
+ ExplainStateStack stack;
/* Must clean up instrumentation state */
InstrEndLoop(instr);
*************** report_triggers(ResultRelInfo *rInfo, bo
*** 496,502 ****
if (instr->ntuples == 0)
continue;
! ExplainOpenGroup("Trigger", NULL, true, es);
relname = RelationGetRelationName(rInfo->ri_RelationDesc);
if (OidIsValid(trig->tgconstraint))
--- 527,533 ----
if (instr->ntuples == 0)
continue;
! ExplainOpenGroup("Trigger", NULL, true, es, &stack);
relname = RelationGetRelationName(rInfo->ri_RelationDesc);
if (OidIsValid(trig->tgconstraint))
*************** report_triggers(ResultRelInfo *rInfo, bo
*** 533,539 ****
if (conname)
pfree(conname);
! ExplainCloseGroup("Trigger", NULL, true, es);
}
}
--- 564,570 ----
if (conname)
pfree(conname);
! ExplainCloseGroup(es, &stack);
}
}
*************** ExplainNode(Plan *plan, PlanState *plans
*** 579,586 ****
const char *sname; /* node type name for non-text output */
const char *strategy = NULL;
const char *operation = NULL;
! int save_indent = es->indent;
! bool haschildren;
Assert(plan);
--- 610,616 ----
const char *sname; /* node type name for non-text output */
const char *strategy = NULL;
const char *operation = NULL;
! ExplainStateStack stack;
Assert(plan);
*************** ExplainNode(Plan *plan, PlanState *plans
*** 731,737 ****
ExplainOpenGroup("Plan",
relationship ? NULL : "Plan",
! true, es);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
--- 761,767 ----
ExplainOpenGroup("Plan",
relationship ? NULL : "Plan",
! true, es, &stack);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
*************** ExplainNode(Plan *plan, PlanState *plans
*** 1041,1047 ****
}
/* Get ready to display the child plans */
! haschildren = plan->initPlan ||
outerPlan(plan) ||
innerPlan(plan) ||
IsA(plan, ModifyTable) ||
--- 1071,1077 ----
}
/* Get ready to display the child plans */
! if (plan->initPlan ||
outerPlan(plan) ||
innerPlan(plan) ||
IsA(plan, ModifyTable) ||
*************** ExplainNode(Plan *plan, PlanState *plans
*** 1049,1135 ****
IsA(plan, BitmapAnd) ||
IsA(plan, BitmapOr) ||
IsA(plan, SubqueryScan) ||
! planstate->subPlan;
! if (haschildren)
! ExplainOpenGroup("Plans", "Plans", false, es);
!
! /* initPlan-s */
! if (plan->initPlan)
! ExplainSubPlans(planstate->initPlan, "InitPlan", es);
!
! /* lefttree */
! if (outerPlan(plan))
{
! /*
! * Ordinarily we don't pass down our own outer_plan value to our child
! * nodes, but in bitmap scan trees we must, since the bottom
! * BitmapIndexScan nodes may have outer references.
! */
! ExplainNode(outerPlan(plan), outerPlanState(planstate),
! IsA(plan, BitmapHeapScan) ? outer_plan : NULL,
! "Outer", NULL, es);
! }
! /* righttree */
! if (innerPlan(plan))
! {
! ExplainNode(innerPlan(plan), innerPlanState(planstate),
! outerPlan(plan),
! "Inner", NULL, es);
! }
! /* special child plans */
! switch (nodeTag(plan))
! {
! case T_ModifyTable:
! ExplainMemberNodes(((ModifyTable *) plan)->plans,
! ((ModifyTableState *) planstate)->mt_plans,
! outer_plan, es);
! break;
! case T_Append:
! ExplainMemberNodes(((Append *) plan)->appendplans,
! ((AppendState *) planstate)->appendplans,
! outer_plan, es);
! break;
! case T_BitmapAnd:
! ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
! ((BitmapAndState *) planstate)->bitmapplans,
! outer_plan, es);
! break;
! case T_BitmapOr:
! ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
! ((BitmapOrState *) planstate)->bitmapplans,
! outer_plan, es);
! break;
! case T_SubqueryScan:
! {
! SubqueryScan *subqueryscan = (SubqueryScan *) plan;
! SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
! ExplainNode(subqueryscan->subplan, subquerystate->subplan,
! NULL,
! "Subquery", NULL, es);
! }
! break;
! default:
! break;
! }
! /* subPlan-s */
! if (planstate->subPlan)
! ExplainSubPlans(planstate->subPlan, "SubPlan", es);
!
! /* end of child plans */
! if (haschildren)
! ExplainCloseGroup("Plans", "Plans", false, es);
! /* in text format, undo whatever indentation we added */
! if (es->format == EXPLAIN_FORMAT_TEXT)
! es->indent = save_indent;
! ExplainCloseGroup("Plan",
! relationship ? NULL : "Plan",
! true, es);
}
/*
--- 1079,1161 ----
IsA(plan, BitmapAnd) ||
IsA(plan, BitmapOr) ||
IsA(plan, SubqueryScan) ||
! planstate->subPlan)
{
! ExplainStateStack child_stack;
! ExplainOpenGroup("Plans", "Plans", false, es, &child_stack);
! /* initPlan-s */
! if (plan->initPlan)
! ExplainSubPlans(planstate->initPlan, "InitPlan", es);
! /* lefttree */
! if (outerPlan(plan))
! {
! /*
! * Ordinarily we don't pass down our own outer_plan value to our child
! * nodes, but in bitmap scan trees we must, since the bottom
! * BitmapIndexScan nodes may have outer references.
! */
! ExplainNode(outerPlan(plan), outerPlanState(planstate),
! IsA(plan, BitmapHeapScan) ? outer_plan : NULL,
! "Outer", NULL, es);
! }
! /* righttree */
! if (innerPlan(plan))
! {
! ExplainNode(innerPlan(plan), innerPlanState(planstate),
! outerPlan(plan),
! "Inner", NULL, es);
! }
! /* special child plans */
! switch (nodeTag(plan))
! {
! case T_ModifyTable:
! ExplainMemberNodes(((ModifyTable *) plan)->plans,
! ((ModifyTableState *) planstate)->mt_plans,
! outer_plan, es);
! break;
! case T_Append:
! ExplainMemberNodes(((Append *) plan)->appendplans,
! ((AppendState *) planstate)->appendplans,
! outer_plan, es);
! break;
! case T_BitmapAnd:
! ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
! ((BitmapAndState *) planstate)->bitmapplans,
! outer_plan, es);
! break;
! case T_BitmapOr:
! ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
! ((BitmapOrState *) planstate)->bitmapplans,
! outer_plan, es);
! break;
! case T_SubqueryScan:
! {
! SubqueryScan *subqueryscan = (SubqueryScan *) plan;
! SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
! ExplainNode(subqueryscan->subplan, subquerystate->subplan,
! NULL,
! "Subquery", NULL, es);
! }
! break;
! default:
! break;
! }
!
! /* subPlan-s */
! if (planstate->subPlan)
! ExplainSubPlans(planstate->subPlan, "SubPlan", es);
!
! /* end of child plans */
! ExplainCloseGroup(es, &child_stack);
! }
!
! ExplainCloseGroup(es, &stack);
}
/*
*************** ExplainPropertyList(const char *qlabel,
*** 1537,1542 ****
--- 1563,1580 ----
}
appendStringInfoChar(es->str, ']');
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, es->indent * 2);
+ appendStringInfo(es->str, "%s:\n", qlabel);
+ foreach(lc, data)
+ {
+ appendStringInfoSpaces(es->str, es->indent * 2 + 2);
+ appendStringInfoString(es->str, "- ");
+ escape_yaml(es->str, (const char *) lfirst(lc));
+ appendStringInfoChar(es->str, '\n');
+ }
+ break;
}
}
*************** static void
*** 1553,1558 ****
--- 1591,1598 ----
ExplainProperty(const char *qlabel, const char *value, bool numeric,
ExplainState *es)
{
+ Assert(es->group != NULL);
+
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
*************** ExplainProperty(const char *qlabel, cons
*** 1584,1589 ****
--- 1624,1644 ----
else
escape_json(es->str, value);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ if (es->group->yaml.firstline)
+ appendStringInfoChar(es->str, ' ');
+ else
+ appendStringInfoSpaces(es->str, es->indent * 2);
+ es->group->yaml.firstline = false;
+ appendStringInfo(es->str, "%s: ", qlabel);
+ if (numeric)
+ appendStringInfoString(es->str, value);
+ else
+ escape_yaml(es->str, value);
+ appendStringInfoChar(es->str, '\n');
+ break;
+
}
}
*************** ExplainPropertyFloat(const char *qlabel,
*** 1636,1647 ****
*/
static void
ExplainOpenGroup(const char *objtype, const char *labelname,
! bool labeled, ExplainState *es)
{
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
! /* nothing to do */
break;
case EXPLAIN_FORMAT_XML:
--- 1691,1709 ----
*/
static void
ExplainOpenGroup(const char *objtype, const char *labelname,
! bool labeled, ExplainState *es, ExplainStateStack *stack)
{
+ ExplainGroup *group = &stack->group;
+
+ memset(group, 0, sizeof(ExplainGroup));
+ stack->objtype = objtype;
+ stack->labeled = labeled;
+ stack->prev = es->group;
+
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
! group->text.save_indent = es->indent;
break;
case EXPLAIN_FORMAT_XML:
*************** ExplainOpenGroup(const char *objtype, co
*** 1660,1674 ****
appendStringInfoChar(es->str, labeled ? '{' : '[');
/*
! * In JSON format, the grouping_stack is an integer list. 0 means
! * we've emitted nothing at this grouping level, 1 means we've
! * emitted something (and so the next item needs a comma).
* See ExplainJSONLineEnding().
*/
! es->grouping_stack = lcons_int(0, es->grouping_stack);
es->indent++;
break;
}
}
/*
--- 1722,1756 ----
appendStringInfoChar(es->str, labeled ? '{' : '[');
/*
! * In JSON format, json.emitted means we've emitted something
! * at this grouping level (and so the next item needs a comma).
* See ExplainJSONLineEnding().
*/
! group->json.emitted = false;
! es->indent++;
! break;
!
! case EXPLAIN_FORMAT_YAML:
! if (es->group->yaml.firstline)
! appendStringInfoChar(es->str, ' ');
! else
! appendStringInfoSpaces(es->str, es->indent * 2);
! es->group->yaml.firstline = false;
! if (labelname)
! {
! appendStringInfo(es->str, "%s:\n", labelname);
! group->yaml.firstline = false;
! }
! else
! {
! appendStringInfoChar(es->str, '-');
! group->yaml.firstline = true;
! }
es->indent++;
break;
}
+
+ es->group = group;
}
/*
*************** ExplainOpenGroup(const char *objtype, co
*** 1676,1703 ****
* Parameters must match the corresponding ExplainOpenGroup call.
*/
static void
! ExplainCloseGroup(const char *objtype, const char *labelname,
! bool labeled, ExplainState *es)
{
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
! /* nothing to do */
break;
case EXPLAIN_FORMAT_XML:
es->indent--;
! ExplainXMLTag(objtype, X_CLOSING, es);
break;
case EXPLAIN_FORMAT_JSON:
es->indent--;
appendStringInfoChar(es->str, '\n');
appendStringInfoSpaces(es->str, 2 * es->indent);
! appendStringInfoChar(es->str, labeled ? '}' : ']');
! es->grouping_stack = list_delete_first(es->grouping_stack);
break;
}
}
/*
--- 1758,1794 ----
* Parameters must match the corresponding ExplainOpenGroup call.
*/
static void
! ExplainCloseGroup(ExplainState *es, const ExplainStateStack *stack)
{
+ Assert(es->group != NULL);
+
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
! /* undo whatever indentation we added */
! es->indent = es->group->text.save_indent;
break;
case EXPLAIN_FORMAT_XML:
es->indent--;
! ExplainXMLTag(stack->objtype, X_CLOSING, es);
break;
case EXPLAIN_FORMAT_JSON:
es->indent--;
appendStringInfoChar(es->str, '\n');
appendStringInfoSpaces(es->str, 2 * es->indent);
! appendStringInfoChar(es->str, stack->labeled ? '}' : ']');
! break;
!
! case EXPLAIN_FORMAT_YAML:
! if (es->group->yaml.firstline)
! appendStringInfoChar(es->str, '\n');
! es->indent--;
break;
}
+
+ es->group = stack->prev;
}
/*
*************** ExplainDummyGroup(const char *objtype, c
*** 1729,1734 ****
--- 1820,1832 ----
}
escape_json(es->str, objtype);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ appendStringInfoSpaces(es->str, es->indent * 2);
+ if (labelname)
+ appendStringInfo(es->str, "%s:", labelname);
+ appendStringInfo(es->str, "%s\n", objtype);
+ break;
}
}
*************** ExplainDummyGroup(const char *objtype, c
*** 1739,1746 ****
* a separate pair of subroutines.
*/
static void
! ExplainBeginOutput(ExplainState *es)
{
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
--- 1837,1850 ----
* a separate pair of subroutines.
*/
static void
! ExplainBeginOutput(ExplainState *es, ExplainStateStack *stack)
{
+ ExplainGroup *group = &stack->group;
+
+ Assert(es->group == NULL);
+
+ memset(stack, 0, sizeof(ExplainStateStack));
+
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
*************** ExplainBeginOutput(ExplainState *es)
*** 1756,1765 ****
case EXPLAIN_FORMAT_JSON:
/* top-level structure is an array of plans */
appendStringInfoChar(es->str, '[');
! es->grouping_stack = lcons_int(0, es->grouping_stack);
es->indent++;
break;
}
}
/*
--- 1860,1875 ----
case EXPLAIN_FORMAT_JSON:
/* top-level structure is an array of plans */
appendStringInfoChar(es->str, '[');
! group->json.emitted = false;
es->indent++;
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ group->yaml.firstline = true;
+ break;
}
+
+ es->group = group;
}
/*
*************** ExplainBeginOutput(ExplainState *es)
*** 1768,1776 ****
--- 1878,1889 ----
static void
ExplainEndOutput(ExplainState *es)
{
+ Assert(es->group != NULL);
+
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
+ case EXPLAIN_FORMAT_YAML:
/* nothing to do */
break;
*************** ExplainEndOutput(ExplainState *es)
*** 1782,1790 ****
case EXPLAIN_FORMAT_JSON:
es->indent--;
appendStringInfoString(es->str, "\n]");
- es->grouping_stack = list_delete_first(es->grouping_stack);
break;
}
}
/*
--- 1895,1904 ----
case EXPLAIN_FORMAT_JSON:
es->indent--;
appendStringInfoString(es->str, "\n]");
break;
}
+
+ es->group = NULL;
}
/*
*************** ExplainSeparatePlans(ExplainState *es)
*** 1801,1806 ****
--- 1915,1921 ----
break;
case EXPLAIN_FORMAT_XML:
+ case EXPLAIN_FORMAT_YAML:
/* nothing to do */
break;
*************** static void
*** 1851,1860 ****
ExplainJSONLineEnding(ExplainState *es)
{
Assert(es->format == EXPLAIN_FORMAT_JSON);
! if (linitial_int(es->grouping_stack) != 0)
appendStringInfoChar(es->str, ',');
else
! linitial_int(es->grouping_stack) = 1;
appendStringInfoChar(es->str, '\n');
}
--- 1966,1975 ----
ExplainJSONLineEnding(ExplainState *es)
{
Assert(es->format == EXPLAIN_FORMAT_JSON);
! if (es->group->json.emitted)
appendStringInfoChar(es->str, ',');
else
! es->group->json.emitted = true;
appendStringInfoChar(es->str, '\n');
}
*************** escape_json(StringInfo buf, const char *
*** 1902,1904 ****
--- 2017,2039 ----
}
appendStringInfoCharMacro(buf, '\"');
}
+
+ /*
+ * YAML is a superset of JSON: if we find quotable characters, we call escape_json
+ */
+ static void
+ escape_yaml(StringInfo buf, const char *str)
+ {
+ const char *p;
+
+ for (p = str; *p; p++)
+ {
+ if ((unsigned char) *p < ' ' || strchr("\"\\\b\f\n\r\t", *p))
+ {
+ escape_json(buf, str);
+ return;
+ }
+ }
+
+ appendStringInfo(buf, "%s", str);
+ }
diff -cprN head/src/include/commands/explain.h work/src/include/commands/explain.h
*** head/src/include/commands/explain.h 2009-08-10 14:46:50.000000000 +0900
--- work/src/include/commands/explain.h 2009-11-16 11:44:45.102407733 +0900
*************** typedef enum ExplainFormat
*** 19,27 ****
{
EXPLAIN_FORMAT_TEXT,
EXPLAIN_FORMAT_XML,
! EXPLAIN_FORMAT_JSON
} ExplainFormat;
typedef struct ExplainState
{
StringInfo str; /* output buffer */
--- 19,30 ----
{
EXPLAIN_FORMAT_TEXT,
EXPLAIN_FORMAT_XML,
! EXPLAIN_FORMAT_JSON,
! EXPLAIN_FORMAT_YAML
} ExplainFormat;
+ typedef union ExplainGroup ExplainGroup;
+
typedef struct ExplainState
{
StringInfo str; /* output buffer */
*************** typedef struct ExplainState
*** 34,40 ****
PlannedStmt *pstmt; /* top of plan */
List *rtable; /* range table */
int indent; /* current indentation level */
! List *grouping_stack; /* format-specific grouping state */
} ExplainState;
/* Hook for plugins to get control in ExplainOneQuery() */
--- 37,44 ----
PlannedStmt *pstmt; /* top of plan */
List *rtable; /* range table */
int indent; /* current indentation level */
!
! ExplainGroup *group; /* format-specific current stack */
} ExplainState;
/* Hook for plugins to get control in ExplainOneQuery() */
Itagaki Takahiro <itagaki.takahiro@oss.ntt.co.jp> wrote:
My rewrite is relatively large. Please reversely-review the patch.
I rethink the code cleanup should be done with another patch even if needed.
Here is a lite version of yaml YAML explan patch.
All of the logic for indent is done in ExplainYAMLLineStarting().
It can remove undesirable linebreaks from the item list and end of output.
Please check whether the v3.1 patch works as your expectation.
If ok, we can move it to "Ready for Committer".
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Attachments:
yaml.explain.v3.1.patchapplication/octet-stream; name=yaml.explain.v3.1.patchDownload
diff -cprN head/contrib/auto_explain/auto_explain.c work/contrib/auto_explain/auto_explain.c
*** head/contrib/auto_explain/auto_explain.c 2009-08-10 14:46:49.000000000 +0900
--- work/contrib/auto_explain/auto_explain.c 2009-11-30 11:54:52.313491403 +0900
*************** static const struct config_enum_entry fo
*** 29,34 ****
--- 29,35 ----
{"text", EXPLAIN_FORMAT_TEXT, false},
{"xml", EXPLAIN_FORMAT_XML, false},
{"json", EXPLAIN_FORMAT_JSON, false},
+ {"yaml", EXPLAIN_FORMAT_YAML, false},
{NULL, 0, false}
};
diff -cprN head/doc/src/sgml/auto-explain.sgml work/doc/src/sgml/auto-explain.sgml
*** head/doc/src/sgml/auto-explain.sgml 2009-08-10 14:46:50.000000000 +0900
--- work/doc/src/sgml/auto-explain.sgml 2009-11-30 11:54:52.314487423 +0900
*************** LOAD 'auto_explain';
*** 114,120 ****
<varname>auto_explain.log_format</varname> selects the
<command>EXPLAIN</> output format to be used.
The allowed values are <literal>text</literal>, <literal>xml</literal>,
! and <literal>json</literal>. The default is text.
Only superusers can change this setting.
</para>
</listitem>
--- 114,120 ----
<varname>auto_explain.log_format</varname> selects the
<command>EXPLAIN</> output format to be used.
The allowed values are <literal>text</literal>, <literal>xml</literal>,
! <literal>json</literal>, and <literal>yaml</literal>. The default is text.
Only superusers can change this setting.
</para>
</listitem>
diff -cprN head/doc/src/sgml/ref/explain.sgml work/doc/src/sgml/ref/explain.sgml
*** head/doc/src/sgml/ref/explain.sgml 2009-08-10 14:46:50.000000000 +0900
--- work/doc/src/sgml/ref/explain.sgml 2009-11-30 11:54:52.314487423 +0900
*************** PostgreSQL documentation
*** 31,37 ****
<refsynopsisdiv>
<synopsis>
! EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> | FORMAT { TEXT | XML | JSON } } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
</synopsis>
</refsynopsisdiv>
--- 31,37 ----
<refsynopsisdiv>
<synopsis>
! EXPLAIN [ ( { ANALYZE <replaceable class="parameter">boolean</replaceable> | VERBOSE <replaceable class="parameter">boolean</replaceable> | COSTS <replaceable class="parameter">boolean</replaceable> | FORMAT { TEXT | XML | JSON | YAML } } [, ...] ) ] <replaceable class="parameter">statement</replaceable>
EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replaceable>
</synopsis>
</refsynopsisdiv>
*************** ROLLBACK;
*** 143,150 ****
<term><literal>FORMAT</literal></term>
<listitem>
<para>
! Specify the output format, which can be TEXT, XML, or JSON.
! XML or JSON output contains the same information as the text output
format, but is easier for programs to parse. This parameter defaults to
<literal>TEXT</literal>.
</para>
--- 143,150 ----
<term><literal>FORMAT</literal></term>
<listitem>
<para>
! Specify the output format, which can be TEXT, XML, JSON, or YAML.
! Non-text output contains the same information as the text output
format, but is easier for programs to parse. This parameter defaults to
<literal>TEXT</literal>.
</para>
diff -cprN head/doc/src/sgml/release-8.5.sgml work/doc/src/sgml/release-8.5.sgml
*** head/doc/src/sgml/release-8.5.sgml 2009-11-27 06:20:12.000000000 +0900
--- work/doc/src/sgml/release-8.5.sgml 2009-11-30 11:54:52.314487423 +0900
***************
*** 176,182 ****
</listitem>
<listitem>
<para>
! EXPLAIN allows output of plans in XML or JSON format for automated
processing of explain plans by analysis or visualization tools.
</para>
</listitem>
--- 176,182 ----
</listitem>
<listitem>
<para>
! EXPLAIN allows output of plans in XML, JSON, or YAML format for automated
processing of explain plans by analysis or visualization tools.
</para>
</listitem>
diff -cprN head/src/backend/commands/explain.c work/src/backend/commands/explain.c
*** head/src/backend/commands/explain.c 2009-11-05 07:26:04.000000000 +0900
--- work/src/backend/commands/explain.c 2009-11-30 12:23:01.926467595 +0900
*************** static void ExplainBeginOutput(ExplainSt
*** 95,101 ****
--- 95,103 ----
static void ExplainEndOutput(ExplainState *es);
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
static void ExplainJSONLineEnding(ExplainState *es);
+ static void ExplainYAMLLineStarting(ExplainState *es);
static void escape_json(StringInfo buf, const char *str);
+ static void escape_yaml(StringInfo buf, const char *str);
/*
*************** ExplainQuery(ExplainStmt *stmt, const ch
*** 135,140 ****
--- 137,144 ----
es.format = EXPLAIN_FORMAT_XML;
else if (strcmp(p, "json") == 0)
es.format = EXPLAIN_FORMAT_JSON;
+ else if (strcmp(p, "yaml") == 0)
+ es.format = EXPLAIN_FORMAT_YAML;
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
*************** ExplainPropertyList(const char *qlabel,
*** 1537,1542 ****
--- 1541,1559 ----
}
appendStringInfoChar(es->str, ']');
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ ExplainYAMLLineStarting(es);
+ escape_yaml(es->str, qlabel);
+ appendStringInfoChar(es->str, ':');
+ foreach(lc, data)
+ {
+ appendStringInfoChar(es->str, '\n');
+ appendStringInfoSpaces(es->str, es->indent * 2 + 2);
+ appendStringInfoString(es->str, "- ");
+ escape_yaml(es->str, (const char *) lfirst(lc));
+ }
+ break;
}
}
*************** ExplainProperty(const char *qlabel, cons
*** 1584,1589 ****
--- 1601,1615 ----
else
escape_json(es->str, value);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ ExplainYAMLLineStarting(es);
+ appendStringInfo(es->str, "%s: ", qlabel);
+ if (numeric)
+ appendStringInfoString(es->str, value);
+ else
+ escape_yaml(es->str, value);
+ break;
}
}
*************** ExplainOpenGroup(const char *objtype, co
*** 1668,1673 ****
--- 1694,1714 ----
es->grouping_stack = lcons_int(0, es->grouping_stack);
es->indent++;
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ ExplainYAMLLineStarting(es);
+ if (labelname)
+ {
+ appendStringInfo(es->str, "%s:", labelname);
+ es->grouping_stack = lcons_int(1, es->grouping_stack);
+ }
+ else
+ {
+ appendStringInfoChar(es->str, '-');
+ es->grouping_stack = lcons_int(0, es->grouping_stack);
+ }
+ es->indent++;
+ break;
}
}
*************** ExplainCloseGroup(const char *objtype, c
*** 1697,1702 ****
--- 1738,1748 ----
appendStringInfoChar(es->str, labeled ? '}' : ']');
es->grouping_stack = list_delete_first(es->grouping_stack);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ es->indent--;
+ es->grouping_stack = list_delete_first(es->grouping_stack);
+ break;
}
}
*************** ExplainDummyGroup(const char *objtype, c
*** 1729,1734 ****
--- 1775,1787 ----
}
escape_json(es->str, objtype);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ ExplainYAMLLineStarting(es);
+ if (labelname)
+ appendStringInfo(es->str, "%s:", labelname);
+ appendStringInfoString(es->str, objtype);
+ break;
}
}
*************** ExplainBeginOutput(ExplainState *es)
*** 1759,1764 ****
--- 1812,1821 ----
es->grouping_stack = lcons_int(0, es->grouping_stack);
es->indent++;
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ es->grouping_stack = lcons_int(0, es->grouping_stack);
+ break;
}
}
*************** ExplainEndOutput(ExplainState *es)
*** 1784,1789 ****
--- 1841,1850 ----
appendStringInfoString(es->str, "\n]");
es->grouping_stack = list_delete_first(es->grouping_stack);
break;
+
+ case EXPLAIN_FORMAT_YAML:
+ es->grouping_stack = list_delete_first(es->grouping_stack);
+ break;
}
}
*************** ExplainSeparatePlans(ExplainState *es)
*** 1796,1801 ****
--- 1857,1863 ----
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
+ case EXPLAIN_FORMAT_YAML:
/* add a blank line */
appendStringInfoChar(es->str, '\n');
break;
*************** ExplainJSONLineEnding(ExplainState *es)
*** 1859,1864 ****
--- 1921,1945 ----
}
/*
+ * Indent a YAML line.
+ */
+ static void
+ ExplainYAMLLineStarting(ExplainState *es)
+ {
+ Assert(es->format == EXPLAIN_FORMAT_YAML);
+ if (linitial_int(es->grouping_stack) == 0)
+ {
+ appendStringInfoChar(es->str, ' ');
+ linitial_int(es->grouping_stack) = 1;
+ }
+ else
+ {
+ appendStringInfoChar(es->str, '\n');
+ appendStringInfoSpaces(es->str, es->indent * 2);
+ }
+ }
+
+ /*
* Produce a JSON string literal, properly escaping characters in the text.
*/
static void
*************** escape_json(StringInfo buf, const char *
*** 1902,1904 ****
--- 1983,2005 ----
}
appendStringInfoCharMacro(buf, '\"');
}
+
+ /*
+ * YAML is a superset of JSON: if we find quotable characters, we call escape_json
+ */
+ static void
+ escape_yaml(StringInfo buf, const char *str)
+ {
+ const char *p;
+
+ for (p = str; *p; p++)
+ {
+ if ((unsigned char) *p < ' ' || strchr("\"\\\b\f\n\r\t", *p))
+ {
+ escape_json(buf, str);
+ return;
+ }
+ }
+
+ appendStringInfo(buf, "%s", str);
+ }
diff -cprN head/src/include/commands/explain.h work/src/include/commands/explain.h
*** head/src/include/commands/explain.h 2009-08-10 14:46:50.000000000 +0900
--- work/src/include/commands/explain.h 2009-11-30 11:54:52.315490662 +0900
*************** typedef enum ExplainFormat
*** 19,25 ****
{
EXPLAIN_FORMAT_TEXT,
EXPLAIN_FORMAT_XML,
! EXPLAIN_FORMAT_JSON
} ExplainFormat;
typedef struct ExplainState
--- 19,26 ----
{
EXPLAIN_FORMAT_TEXT,
EXPLAIN_FORMAT_XML,
! EXPLAIN_FORMAT_JSON,
! EXPLAIN_FORMAT_YAML
} ExplainFormat;
typedef struct ExplainState