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 ****
auto_explain.log_format selects the
EXPLAIN> output format to be used.
The allowed values are text, xml,
! and json. The default is text.
Only superusers can change this setting.
--- 114,120 ----
auto_explain.log_format selects the
EXPLAIN> output format to be used.
The allowed values are text, xml,
! json, and yaml. The default is text.
Only superusers can change this setting.
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 ****
! EXPLAIN [ ( { ANALYZE boolean | VERBOSE boolean | COSTS boolean | FORMAT { TEXT | XML | JSON } } [, ...] ) ] statement
EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
--- 31,37 ----
! EXPLAIN [ ( { ANALYZE boolean | VERBOSE boolean | COSTS boolean | FORMAT { TEXT | XML | JSON | YAML } } [, ...] ) ] statement
EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
*************** ROLLBACK;
*** 143,150 ****
FORMAT
! 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
TEXT.
--- 143,150 ----
FORMAT
! 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
TEXT.
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 ****
! EXPLAIN allows output of plans in XML or JSON format for automated
processing of explain plans by analysis or visualization tools.
--- 176,182 ----
! EXPLAIN allows output of plans in XML, JSON, or YAML format for automated
processing of explain plans by analysis or visualization tools.
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() */