*** ./doc/src/sgml/ref/explain.sgml.orig 2008-10-09 11:22:15.000000000 -0700
--- ./doc/src/sgml/ref/explain.sgml 2008-10-09 11:23:09.000000000 -0700
***************
*** 30,36 ****
! EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
--- 30,36 ----
! EXPLAIN [ ANALYZE ] [ VERBOSE ] [ XML ] statement
*** ./src/backend/commands/explain.c.orig 2008-01-01 11:45:49.000000000 -0800
--- ./src/backend/commands/explain.c 2008-09-22 15:11:50.000000000 -0700
***************
*** 45,50 ****
--- 45,51 ----
/* options */
bool printNodes; /* do nodeToString() too */
bool printAnalyze; /* print actual times */
+ bool printXML; /* print output as XML */
/* other states */
PlannedStmt *pstmt; /* top of plan */
List *rtable; /* range table */
***************
*** 54,60 ****
const char *queryString,
ParamListInfo params, TupOutputState *tstate);
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
! StringInfo buf);
static double elapsed_time(instr_time *starttime);
static void explain_outNode(StringInfo str,
Plan *plan, PlanState *planstate,
--- 55,61 ----
const char *queryString,
ParamListInfo params, TupOutputState *tstate);
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
! StringInfo buf, bool show_xml);
static double elapsed_time(instr_time *starttime);
static void explain_outNode(StringInfo str,
Plan *plan, PlanState *planstate,
***************
*** 71,76 ****
--- 72,79 ----
static void show_sort_info(SortState *sortstate,
StringInfo str, int indent, ExplainState *es);
static const char *explain_get_index_name(Oid indexId);
+ static char* encodeXML(char* str);
+
/*
***************
*** 167,173 ****
PlannedStmt *plan;
/* plan the query */
! plan = planner(query, 0, params);
/* run it (if needed) and produce output */
ExplainOnePlan(plan, params, stmt, tstate);
--- 170,179 ----
PlannedStmt *plan;
/* plan the query */
! if (stmt->xml)
! plan = planner(query, CURSOR_OPT_EMIT_PLANNER_XML, params);
! else
! plan = planner(query, 0, params);
/* run it (if needed) and produce output */
ExplainOnePlan(plan, params, stmt, tstate);
***************
*** 224,229 ****
--- 230,237 ----
double totaltime = 0;
ExplainState *es;
StringInfoData buf;
+ ListCell *lcl;
+ Plan *chosen_plan;
int eflags;
/*
***************
*** 253,278 ****
else
eflags = EXEC_FLAG_EXPLAIN_ONLY;
- /* call ExecutorStart to prepare the plan for execution */
- ExecutorStart(queryDesc, eflags);
-
- /* Execute the plan for statistics if asked for */
- if (stmt->analyze)
- {
- /* run the plan */
- ExecutorRun(queryDesc, ForwardScanDirection, 0L);
-
- /* We can't clean up 'till we're done printing the stats... */
- totaltime += elapsed_time(&starttime);
- }
-
es = (ExplainState *) palloc0(sizeof(ExplainState));
es->printNodes = stmt->verbose;
es->printAnalyze = stmt->analyze;
es->pstmt = queryDesc->plannedstmt;
es->rtable = queryDesc->plannedstmt->rtable;
if (es->printNodes)
{
char *s;
--- 261,319 ----
else
eflags = EXEC_FLAG_EXPLAIN_ONLY;
es = (ExplainState *) palloc0(sizeof(ExplainState));
es->printNodes = stmt->verbose;
es->printAnalyze = stmt->analyze;
+ es->printXML = stmt->xml;
es->pstmt = queryDesc->plannedstmt;
es->rtable = queryDesc->plannedstmt->rtable;
+ initStringInfo(&buf);
+
+ if (stmt->xml) {
+ appendStringInfo (&buf, "\n\n");
+
+ /* Only include the DTD if the user *really* wants it */
+ //if (stmt->dtd)
+ // show_dtd(&buf);
+
+ appendStringInfo (&buf, "\n", encodeXML(PG_VERSION));
+ }
+
+ /* Store this for later */
+ chosen_plan = plannedstmt->planTree;
+
+ /*
+ * Show off the discarded path/plans first before showing the most optimal plan */
+ if (stmt->xml) {
+
+ foreach(lcl, queryDesc->plannedstmt->roi_plans)
+ {
+ Roi *roi = (Roi*)lfirst(lcl);
+ ListCell *lcl2;
+ appendStringInfo(&buf, "\n", roi->id->data);
+ foreach(lcl2, roi->plans)
+ {
+
+ Plan *plan = (Plan*)lfirst(lcl2);
+ if (plan != NULL)
+ {
+ plannedstmt->planTree = plan;
+ ExecutorStart(queryDesc, eflags);
+
+ explain_outNode(&buf, plan, queryDesc->planstate, NULL, 1, es);
+
+ ExecutorEnd(queryDesc);
+ }
+
+ }
+ appendStringInfo(&buf, "\n");
+
+
+ }
+ }
+
if (es->printNodes)
{
char *s;
***************
*** 292,301 ****
}
}
! initStringInfo(&buf);
! explain_outNode(&buf,
! queryDesc->plannedstmt->planTree, queryDesc->planstate,
NULL, 0, es);
/*
* If we ran the command, run any AFTER triggers it queued. (Note this
--- 333,361 ----
}
}
! /* Restore the original plan value */
! plannedstmt->planTree = chosen_plan;
!
! /* call ExecutorStart to prepare the plan for execution */
! ExecutorStart(queryDesc, eflags);
!
! /* Execute the plan for statistics if asked for */
! if (stmt->analyze)
! {
! /* run the plan */
! ExecutorRun(queryDesc, ForwardScanDirection, 0L);
!
! /* We can't clean up 'till we're done printing the stats... */
! totaltime += elapsed_time(&starttime);
! }
!
! if (stmt->xml)
! appendStringInfo(&buf, "\n");
!
! explain_outNode(&buf, queryDesc->plannedstmt->planTree, queryDesc->planstate,
NULL, 0, es);
+ if (stmt->xml)
+ appendStringInfo(&buf, "\n");
/*
* If we ran the command, run any AFTER triggers it queued. (Note this
***************
*** 322,333 ****
show_relname = (numrels > 1 || targrels != NIL);
rInfo = queryDesc->estate->es_result_relations;
for (nr = 0; nr < numrels; rInfo++, nr++)
! report_triggers(rInfo, show_relname, &buf);
foreach(l, targrels)
{
rInfo = (ResultRelInfo *) lfirst(l);
! report_triggers(rInfo, show_relname, &buf);
}
}
--- 382,393 ----
show_relname = (numrels > 1 || targrels != NIL);
rInfo = queryDesc->estate->es_result_relations;
for (nr = 0; nr < numrels; rInfo++, nr++)
! report_triggers(rInfo, show_relname, &buf, stmt->xml);
foreach(l, targrels)
{
rInfo = (ResultRelInfo *) lfirst(l);
! report_triggers(rInfo, show_relname, &buf, stmt->xml);
}
}
***************
*** 348,355 ****
--- 408,424 ----
totaltime += elapsed_time(&starttime);
if (stmt->analyze)
+ {
+ if (stmt->xml)
+ appendStringInfo(&buf, "\n",
+ 1000.0 * totaltime);
+ else
appendStringInfo(&buf, "Total runtime: %.3f ms\n",
1000.0 * totaltime);
+ }
+ if (stmt->xml)
+ appendStringInfo(&buf, "\n");
+
do_text_output_multiline(tstate, buf.data);
pfree(buf.data);
***************
*** 361,367 ****
* report execution stats for a single relation's triggers
*/
static void
! report_triggers(ResultRelInfo *rInfo, bool show_relname, StringInfo buf)
{
int nt;
--- 430,436 ----
* report execution stats for a single relation's triggers
*/
static void
! report_triggers(ResultRelInfo *rInfo, bool show_relname, StringInfo buf, bool show_xml)
{
int nt;
***************
*** 372,377 ****
--- 441,448 ----
Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
char *conname;
+ StringInfo triggerStr;
+ triggerStr = makeStringInfo();
/* Must clean up instrumentation state */
InstrEndLoop(instr);
***************
*** 386,403 ****
if (OidIsValid(trig->tgconstraint) &&
(conname = get_constraint_name(trig->tgconstraint)) != NULL)
{
! appendStringInfo(buf, "Trigger for constraint %s", conname);
pfree(conname);
}
! else
appendStringInfo(buf, "Trigger %s", trig->tgname);
if (show_relname)
appendStringInfo(buf, " on %s",
RelationGetRelationName(rInfo->ri_RelationDesc));
appendStringInfo(buf, ": time=%.3f calls=%.0f\n",
1000.0 * instr->total, instr->ntuples);
}
}
--- 457,497 ----
if (OidIsValid(trig->tgconstraint) &&
(conname = get_constraint_name(trig->tgconstraint)) != NULL)
{
! if (!show_xml)
! appendStringInfo(buf, "Trigger for constraint %s", conname);
! else
! appendStringInfo(triggerStr, "constraint=\"%s\"", encodeXML(conname));
pfree(conname);
}
! else {
! if (!show_xml)
appendStringInfo(buf, "Trigger %s", trig->tgname);
+ else
+ appendStringInfo(triggerStr, "name=\"%s\"", encodeXML(trig->tgname));
+ }
if (show_relname)
+ {
+ if (!show_xml)
appendStringInfo(buf, " on %s",
RelationGetRelationName(rInfo->ri_RelationDesc));
+ else
+ appendStringInfo(triggerStr, " on=\"%s\"",
+ encodeXML(RelationGetRelationName(rInfo->ri_RelationDesc)));
+
+ }
+ if (show_xml)
+ appendStringInfo(buf, " \n",
+ encodeXML(triggerStr->data),
+ 1000.0 * instr->total,
+ instr->ntuples);
+ else
appendStringInfo(buf, ": time=%.3f calls=%.0f\n",
1000.0 * instr->total, instr->ntuples);
+ pfree(triggerStr->data);
+ pfree(triggerStr);
}
}
***************
*** 447,453 ****
if (plan == NULL)
{
! appendStringInfoChar(str, '\n');
return;
}
--- 541,550 ----
if (plan == NULL)
{
! if (es->printXML)
! appendStringInfo(str, "\n");
! else
! appendStringInfoChar(str, '\n');
return;
}
***************
*** 618,631 ****
break;
}
! appendStringInfoString(str, pname);
switch (nodeTag(plan))
{
case T_IndexScan:
if (ScanDirectionIsBackward(((IndexScan *) plan)->indexorderdir))
appendStringInfoString(str, " Backward");
appendStringInfo(str, " using %s",
explain_get_index_name(((IndexScan *) plan)->indexid));
/* FALL THRU */
case T_SeqScan:
case T_BitmapHeapScan:
--- 715,777 ----
break;
}
! if (es->printXML)
! {
! for (i = 0; i < indent; i++)
! appendStringInfo(str, " ");
! if (plan->id != NULL)
! appendStringInfo(str, "id->data);
! else
! appendStringInfo(str, "isdeleted)
! appendStringInfo(str, "del=\"true\"");
! appendStringInfo(str, " \>\n");
!
! if (plan->pathkeys != NULL && strlen(plan->pathkeys) > 0)
! {
! /* Catch the path keys */
! for (i = 0; i < indent; i++)
! appendStringInfo(str, " ");
! appendStringInfo(str, " \n", encodeXML(plan->pathkeys));
! }
! }
! else
! appendStringInfoString(str, pname);
switch (nodeTag(plan))
{
case T_IndexScan:
+ {
+ StringInfo index;
+ index = makeStringInfo();
+ appendStringInfo(index, "name=\"%s\"", explain_get_index_name(((IndexScan *) plan)->indexid));
+
if (ScanDirectionIsBackward(((IndexScan *) plan)->indexorderdir))
+ {
+ if (es->printXML)
+ appendStringInfoString(index, " sd=\"backward\" ");
+ else
appendStringInfoString(str, " Backward");
+ }
+ else
+ {
+ if (es->printXML)
+ appendStringInfoString(index, " sd=\"forward\" ");
+
+ }
+
+ if (es->printXML)
+ {
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " \n",
+ index->data);
+ }
+ else
appendStringInfo(str, " using %s",
explain_get_index_name(((IndexScan *) plan)->indexid));
+ pfree(index->data);
+ pfree(index);
+ }
/* FALL THRU */
case T_SeqScan:
case T_BitmapHeapScan:
***************
*** 635,640 ****
--- 781,789 ----
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
char *relname;
+ StringInfo resname;
+
+ resname = makeStringInfo();
/* Assume it's on a real relation */
Assert(rte->rtekind == RTE_RELATION);
***************
*** 642,655 ****
--- 791,837 ----
/* We only show the rel name, not schema name */
relname = get_rel_name(rte->relid);
+ if (es->printXML)
+ {
+ appendStringInfo(resname, "name=\"%s\"",
+ encodeXML(quote_identifier(relname)));
+ } else
+ {
appendStringInfo(str, " on %s",
quote_identifier(relname));
+ }
+
+
if (strcmp(rte->eref->aliasname, relname) != 0)
+ {
+ if (es->printXML)
+ appendStringInfo(resname, " alias=\"%s\"",
+ encodeXML(quote_identifier(rte->eref->aliasname)));
+ else
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
}
+ if (es->printXML)
+ {
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " \n",
+ resname->data);
+ }
+
+ pfree(resname->data);
+ pfree(resname);
+ }
break;
case T_BitmapIndexScan:
+ if (es->printXML)
+ {
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " \n",
+ encodeXML(explain_get_index_name(((BitmapIndexScan *) plan)->indexid)));
+ }
+ else
appendStringInfo(str, " on %s",
explain_get_index_name(((BitmapIndexScan *) plan)->indexid));
break;
***************
*** 659,664 ****
--- 841,854 ----
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
+ if (es->printXML)
+ {
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " \n",
+ encodeXML(quote_identifier(rte->eref->aliasname)));
+ }
+ else
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
}
***************
*** 671,676 ****
--- 861,870 ----
Node *funcexpr;
char *proname;
+ StringInfo resname;
+
+ resname = makeStringInfo();
+
/* Assert it's on a RangeFunction */
Assert(rte->rtekind == RTE_FUNCTION);
***************
*** 691,701 ****
--- 885,916 ----
else
proname = rte->eref->aliasname;
+ if (es->printXML)
+ appendStringInfo(resname, "name=\"%s\"",
+ encodeXML(quote_identifier(proname)));
+ else
appendStringInfo(str, " on %s",
quote_identifier(proname));
if (strcmp(rte->eref->aliasname, proname) != 0)
+ {
+ if (es->printXML)
+ appendStringInfo(resname, " alias=\"%s\"",
+ encodeXML(quote_identifier(rte->eref->aliasname)));
+ else
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
+ }
+
+ if (es->printXML)
+ {
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " \n",
+ encodeXML(resname->data));
+ }
+ pfree(resname->data);
+ pfree(resname);
+
}
break;
case T_ValuesScan:
***************
*** 710,716 ****
valsname = rte->eref->aliasname;
! appendStringInfo(str, " on %s",
quote_identifier(valsname));
}
break;
--- 925,935 ----
valsname = rte->eref->aliasname;
! if (es->printXML)
! appendStringInfo(str, "name=\"%s\"",
! encodeXML(quote_identifier(valsname)));
! else
! appendStringInfo(str, " on %s",
quote_identifier(valsname));
}
break;
***************
*** 718,723 ****
--- 937,952 ----
break;
}
+ if (es->printXML)
+ {
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " \n",
+ plan->startup_cost, plan->total_cost,
+ plan->plan_rows, plan->plan_width);
+ }
+ else
appendStringInfo(str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
plan->startup_cost, plan->total_cost,
plan->plan_rows, plan->plan_width);
***************
*** 733,747 ****
{
double nloops = planstate->instrument->nloops;
! appendStringInfo(str, " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
1000.0 * planstate->instrument->startup / nloops,
1000.0 * planstate->instrument->total / nloops,
planstate->instrument->ntuples / nloops,
planstate->instrument->nloops);
}
else if (es->printAnalyze)
appendStringInfo(str, " (never executed)");
! appendStringInfoChar(str, '\n');
/* quals, sort keys, etc */
switch (nodeTag(plan))
--- 962,996 ----
{
double nloops = planstate->instrument->nloops;
! if (es->printXML)
! {
! for (i = 0; i < indent; i++)
! appendStringInfo(str, " ");
! appendStringInfo(str,
! " \n",
! 1000.0 * planstate->instrument->startup / nloops,
! 1000.0 * planstate->instrument->total / nloops,
! planstate->instrument->ntuples / nloops,
! planstate->instrument->nloops);
! }
! else
! appendStringInfo(str, " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
1000.0 * planstate->instrument->startup / nloops,
1000.0 * planstate->instrument->total / nloops,
planstate->instrument->ntuples / nloops,
planstate->instrument->nloops);
}
else if (es->printAnalyze)
+ {
+ if (es->printXML)
+ appendStringInfo(str, " ");
+ else
appendStringInfo(str, " (never executed)");
! }
!
! if (!es->printXML)
! appendStringInfoChar(str, '\n');
/* quals, sort keys, etc */
switch (nodeTag(plan))
***************
*** 876,890 ****
--- 1125,1148 ----
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
+
+ if (!es->printXML)
+ {
appendStringInfo(str, " InitPlan\n");
+ }
+
foreach(lst, planstate->initPlan)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
+ if (!es->printXML)
+ {
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
+ }
+
explain_outNode(str,
exec_subplan_get_plan(es->pstmt, sp),
sps->planstate,
***************
*** 896,904 ****
--- 1154,1165 ----
/* lefttree */
if (outerPlan(plan))
{
+ if (!es->printXML)
+ {
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
+ }
/*
* Ordinarily we don't pass down our own outer_plan value to our child
***************
*** 914,922 ****
/* righttree */
if (innerPlan(plan))
{
! for (i = 0; i < indent; i++)
! appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, innerPlan(plan),
innerPlanState(planstate),
outerPlan(plan),
--- 1175,1187 ----
/* righttree */
if (innerPlan(plan))
{
! if (!es->printXML)
! {
! for (i = 0; i < indent; i++)
! appendStringInfo(str, " ");
!
appendStringInfo(str, " -> ");
+ }
explain_outNode(str, innerPlan(plan),
innerPlanState(planstate),
outerPlan(plan),
***************
*** 935,943 ****
--- 1200,1211 ----
{
Plan *subnode = (Plan *) lfirst(lst);
+ if (!es->printXML)
+ {
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
+ }
/*
* Ordinarily we don't pass down our own outer_plan value to our
***************
*** 965,973 ****
--- 1233,1244 ----
{
Plan *subnode = (Plan *) lfirst(lst);
+ if (!es->printXML)
+ {
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
+ }
explain_outNode(str, subnode,
bitmapandstate->bitmapplans[j],
***************
*** 989,997 ****
--- 1260,1271 ----
{
Plan *subnode = (Plan *) lfirst(lst);
+ if (!es->printXML)
+ {
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
+ }
explain_outNode(str, subnode,
bitmaporstate->bitmapplans[j],
***************
*** 1007,1015 ****
--- 1281,1292 ----
SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
Plan *subnode = subqueryscan->subplan;
+ if (!es->printXML)
+ {
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
+ }
explain_outNode(str, subnode,
subquerystate->subplan,
***************
*** 1024,1038 ****
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
! appendStringInfo(str, " SubPlan\n");
foreach(lst, planstate->subPlan)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str,
exec_subplan_get_plan(es->pstmt, sp),
sps->planstate,
--- 1301,1322 ----
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
!
! if (!es->printXML)
! {
! appendStringInfo(str, " SubPlan\n");
! }
foreach(lst, planstate->subPlan)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
+ if (!es->printXML)
+ {
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
+ }
explain_outNode(str,
exec_subplan_get_plan(es->pstmt, sp),
sps->planstate,
***************
*** 1040,1053 ****
indent + 4, es);
}
}
}
/*
! * Show a qualifier expression for a scan plan node
! *
! * Note: outer_plan is the referent for any OUTER vars in the scan qual;
! * this would be the outer side of a nestloop plan. inner_plan should be
! * NULL except for a SubqueryScan plan node, where it should be the subplan.
*/
static void
show_scan_qual(List *qual, const char *qlabel,
--- 1324,1339 ----
indent + 4, es);
}
}
+ if (es->printXML)
+ {
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, "\n");
+ }
}
/*
! * Show the targetlist of a plan node
*/
static void
show_scan_qual(List *qual, const char *qlabel,
***************
*** 1076,1085 ****
/* Deparse the expression */
exprstr = deparse_expression(node, context, useprefix, false);
! /* And add to str */
! for (i = 0; i < indent; i++)
! appendStringInfo(str, " ");
! appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
}
/*
--- 1362,1375 ----
/* Deparse the expression */
exprstr = deparse_expression(node, context, useprefix, false);
! if (es->printXML)
! appendStringInfo(str," \n",
! encodeXML(qlabel), encodeXML(exprstr));
! else
! {
!
! appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
! }
}
/*
***************
*** 1112,1118 ****
--- 1402,1415 ----
/* And add to str */
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
+ if (es->printXML)
+ appendStringInfo(str," \n",
+ encodeXML(qlabel), encodeXML(exprstr));
+
+ else
+ {
appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
+ }
}
/*
***************
*** 1128,1140 ****
int keyno;
char *exprstr;
int i;
if (nkeys <= 0)
return;
! for (i = 0; i < indent; i++)
! appendStringInfo(str, " ");
! appendStringInfo(str, " %s: ", qlabel);
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) outerPlan(sortplan),
--- 1425,1442 ----
int keyno;
char *exprstr;
int i;
+ StringInfo condition;
if (nkeys <= 0)
return;
! if (!es->printXML)
! {
! for (i = 0; i < indent; i++)
! appendStringInfo(str, " ");
!
! appendStringInfo(str, " %s: ", qlabel);
! }
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) outerPlan(sortplan),
***************
*** 1142,1147 ****
--- 1444,1451 ----
es->rtable);
useprefix = list_length(es->rtable) > 1;
+ condition = makeStringInfo();
+
for (keyno = 0; keyno < nkeys; keyno++)
{
/* find key expression in tlist */
***************
*** 1153,1165 ****
/* Deparse the expression, showing any top-level cast */
exprstr = deparse_expression((Node *) target->expr, context,
useprefix, true);
/* And add to str */
if (keyno > 0)
appendStringInfo(str, ", ");
appendStringInfoString(str, exprstr);
}
! appendStringInfo(str, "\n");
}
/*
--- 1457,1491 ----
/* Deparse the expression, showing any top-level cast */
exprstr = deparse_expression((Node *) target->expr, context,
useprefix, true);
+ if (es->printXML) {
+ /* Add to condition */
+ if (keyno > 0)
+ appendStringInfo(condition, ", ");
+ appendStringInfo(condition, encodeXML(exprstr));
+ }
+ else
+ {
/* And add to str */
if (keyno > 0)
appendStringInfo(str, ", ");
appendStringInfoString(str, exprstr);
+ }
+ }
+
+ if (es->printXML)
+ {
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+
+ appendStringInfo(str," \n",
+ encodeXML(qlabel), encodeXML(condition->data));
+
}
+ else
+ appendStringInfo(str, "\n");
! pfree(condition->data);
! pfree(condition);
}
/*
***************
*** 1209,1211 ****
--- 1535,1589 ----
}
return result;
}
+ /*
+ * Encode XML string
+ * The following are reserved characters and need to be replaced
+ * with XML encoded versions.
+ * '&' -> '&' '<' -> '<' '>' -> '>' '"' -> '"' ''' -> '
+ *
+ */
+
+ static char*
+ encodeXML(char* str)
+ {
+ StringInfo enstr;
+ int i;
+ int len;
+
+ if (str == NULL)
+ return "";
+
+ enstr = makeStringInfo();
+
+ len = strlen(str);
+
+ for (i=0; i< len; i++)
+ {
+ switch(str[i])
+ {
+ case '&':
+ appendStringInfo(enstr, "&");
+ break;
+ case '<':
+ appendStringInfo(enstr, "<");
+ break;
+ case '>':
+ appendStringInfo(enstr, ">");
+ break;
+ case '"':
+ appendStringInfo(enstr, """);
+ break;
+ case '\'':
+ appendStringInfo(enstr, "'");
+ break;
+ default:
+ appendStringInfoChar(enstr, str[i]);
+ }
+
+
+ }
+
+ return enstr->data;
+
+
+ }
*** ./src/backend/nodes/copyfuncs.c.orig 2008-09-21 13:02:19.000000000 -0700
--- ./src/backend/nodes/copyfuncs.c 2008-09-21 13:04:00.000000000 -0700
***************
*** 2565,2570 ****
--- 2565,2571 ----
COPY_NODE_FIELD(query);
COPY_SCALAR_FIELD(verbose);
COPY_SCALAR_FIELD(analyze);
+ COPY_SCALAR_FIELD(xml);
return newnode;
}
*** ./src/backend/nodes/equalfuncs.c.orig 2008-09-21 13:04:12.000000000 -0700
--- ./src/backend/nodes/equalfuncs.c 2008-09-21 13:05:11.000000000 -0700
***************
*** 1358,1363 ****
--- 1358,1364 ----
COMPARE_NODE_FIELD(query);
COMPARE_SCALAR_FIELD(verbose);
COMPARE_SCALAR_FIELD(analyze);
+ COMPARE_SCALAR_FIELD(xml);
return true;
}
*** ./src/backend/nodes/print.c.orig 2008-09-21 13:05:23.000000000 -0700
--- ./src/backend/nodes/print.c 2008-09-21 13:07:31.000000000 -0700
***************
*** 20,25 ****
--- 20,26 ----
#include "postgres.h"
#include "access/printtup.h"
+ #include "lib/stringinfo.h"
#include "nodes/print.h"
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
***************
*** 303,315 ****
* print_expr
* print an expression
*/
! void
print_expr(Node *expr, List *rtable)
{
if (expr == NULL)
{
! printf("<>");
! return;
}
if (IsA(expr, Var))
--- 304,321 ----
* print_expr
* print an expression
*/
! char*
print_expr(Node *expr, List *rtable)
{
+ StringInfo str = NULL;
+
+ str = makeStringInfo();
+
if (expr == NULL)
{
! appendStringInfo(str, "<>");
! printf("%s", str->data);
! return str->data;
}
if (IsA(expr, Var))
***************
*** 340,345 ****
--- 346,352 ----
}
break;
}
+ appendStringInfo(str, "%s.%s", relname, attname);
printf("%s.%s", relname, attname);
}
else if (IsA(expr, Const))
***************
*** 352,358 ****
if (c->constisnull)
{
printf("NULL");
! return;
}
getTypeOutputInfo(c->consttype,
--- 359,366 ----
if (c->constisnull)
{
printf("NULL");
! appendStringInfo(str, "NULL");
! return str->data;
}
getTypeOutputInfo(c->consttype,
***************
*** 360,365 ****
--- 368,374 ----
outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
printf("%s", outputstr);
+ appendStringInfo(str, "%s", outputstr);
pfree(outputstr);
}
else if (IsA(expr, OpExpr))
***************
*** 370,384 ****
opname = get_opname(e->opno);
if (list_length(e->args) > 1)
{
! print_expr(get_leftop((Expr *) e), rtable);
printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
! print_expr(get_rightop((Expr *) e), rtable);
}
else
{
/* we print prefix and postfix ops the same... */
printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
! print_expr(get_leftop((Expr *) e), rtable);
}
}
else if (IsA(expr, FuncExpr))
--- 379,395 ----
opname = get_opname(e->opno);
if (list_length(e->args) > 1)
{
! appendStringInfo(str, "%s", print_expr(get_leftop((Expr *) e), rtable));
printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
! appendStringInfo(str," %s ", ((opname != NULL) ? opname : "(invalid operator)"));
! appendStringInfo(str, "%s", print_expr(get_rightop((Expr *) e), rtable));
}
else
{
/* we print prefix and postfix ops the same... */
printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
! appendStringInfo(str,"%s ", ((opname != NULL) ? opname : "(invalid operator)"));
! appendStringInfo(str,"%s", print_expr(get_leftop((Expr *) e), rtable));
}
}
else if (IsA(expr, FuncExpr))
***************
*** 389,414 ****
funcname = get_func_name(e->funcid);
printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
foreach(l, e->args)
{
print_expr(lfirst(l), rtable);
! if (lnext(l))
printf(",");
}
printf(")");
}
! else
printf("unknown expr");
}
/*
* print_pathkeys -
* pathkeys list of PathKeys
*/
! void
print_pathkeys(List *pathkeys, List *rtable)
{
ListCell *i;
printf("(");
foreach(i, pathkeys)
--- 400,435 ----
funcname = get_func_name(e->funcid);
printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
+ appendStringInfo(str,"%s", ((funcname != NULL) ? funcname : "(invalid function)"));
foreach(l, e->args)
{
print_expr(lfirst(l), rtable);
! if (lnext(l)) {
! appendStringInfo(str, ",");
printf(",");
+ }
}
printf(")");
}
! else {
! appendStringInfo(str, "unkown expr");
printf("unknown expr");
+ }
+
+ return str->data;
}
/*
* print_pathkeys -
* pathkeys list of PathKeys
*/
! char*
print_pathkeys(List *pathkeys, List *rtable)
{
ListCell *i;
+ StringInfo str=NULL;
+
+ str = makeStringInfo();
printf("(");
foreach(i, pathkeys)
***************
*** 423,428 ****
--- 444,450 ----
while (eclass->ec_merged)
eclass = eclass->ec_merged;
+ appendStringInfoString(str, "(");
printf("(");
foreach(k, eclass->ec_members)
{
***************
*** 430,444 ****
if (first)
first = false;
! else
printf(", ");
! print_expr((Node *) mem->em_expr, rtable);
}
printf(")");
! if (lnext(i))
printf(", ");
}
printf(")\n");
}
/*
--- 452,473 ----
if (first)
first = false;
! else {
printf(", ");
! appendStringInfoString(str, ", ");
!
! }
! appendStringInfo(str, "%s", print_expr((Node *) mem->em_expr, rtable));
}
+ appendStringInfoString(str, ")");
printf(")");
! if (lnext(i)) {
! appendStringInfoString(str, ", ");
printf(", ");
+ }
}
printf(")\n");
+ return str->data;
}
/*
*** ./src/backend/optimizer/path/allpaths.c.orig 2008-09-21 12:42:45.000000000 -0700
--- ./src/backend/optimizer/path/allpaths.c 2008-09-21 12:48:51.000000000 -0700
***************
*** 31,36 ****
--- 31,37 ----
#include "parser/parse_expr.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+ #include "lib/stringinfo.h"
/* These parameters are set by GUC */
***************
*** 48,54 ****
RangeTblEntry *rte);
static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
! static void set_dummy_rel_pathlist(RelOptInfo *rel);
static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
--- 49,55 ----
RangeTblEntry *rte);
static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
! static void set_dummy_rel_pathlist(RelOptInfo *rel, int options);
static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
***************
*** 68,73 ****
--- 69,75 ----
RangeTblEntry *rte, Index rti, Node *qual);
static void recurse_push_qual(Node *setOp, Query *topquery,
RangeTblEntry *rte, Index rti, Node *qual);
+ static char* print_relids_nd(Relids relids, PlannerInfo* root);
/*
***************
*** 186,191 ****
--- 188,204 ----
set_plain_rel_pathlist(root, rel, rte);
}
+ /* Add the RelOptInfo pointer rel to the
+ * root->rois list for later use if we have the
+ * planner output bit set
+ */
+ if (root->glob->options & CURSOR_OPT_EMIT_PLANNER_XML)
+ {
+ rel->id = makeStringInfo();
+ appendStringInfo(rel->id, "%s", print_relids_nd(rel->relids, root));
+ root->rois = lappend(root->rois, rel);
+ }
+
#ifdef OPTIMIZER_DEBUG
debug_print_rel(root, rel);
#endif
***************
*** 207,213 ****
if (rel->reloptkind == RELOPT_BASEREL &&
relation_excluded_by_constraints(root, rel, rte))
{
! set_dummy_rel_pathlist(rel);
return;
}
--- 220,226 ----
if (rel->reloptkind == RELOPT_BASEREL &&
relation_excluded_by_constraints(root, rel, rte))
{
! set_dummy_rel_pathlist(rel, root->glob->options);
return;
}
***************
*** 234,240 ****
*/
/* Consider sequential scan */
! add_path(rel, create_seqscan_path(root, rel));
/* Consider index scans */
create_index_paths(root, rel);
--- 247,253 ----
*/
/* Consider sequential scan */
! add_path(rel, create_seqscan_path(root, rel), root->glob->options);
/* Consider index scans */
create_index_paths(root, rel);
***************
*** 328,334 ****
* appendrel. Mark it with a dummy cheapest-path though, in case
* best_appendrel_indexscan() looks at it later.
*/
! set_dummy_rel_pathlist(childrel);
continue;
}
--- 341,347 ----
* appendrel. Mark it with a dummy cheapest-path though, in case
* best_appendrel_indexscan() looks at it later.
*/
! set_dummy_rel_pathlist(childrel, root->glob->options);
continue;
}
***************
*** 417,423 ****
* the parent rel. (Note: this is correct even if we have zero or one
* live subpath due to constraint exclusion.)
*/
! add_path(rel, (Path *) create_append_path(rel, subpaths));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
--- 430,436 ----
* the parent rel. (Note: this is correct even if we have zero or one
* live subpath due to constraint exclusion.)
*/
! add_path(rel, (Path *) create_append_path(rel, subpaths), root->glob->options);
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
***************
*** 431,443 ****
* AppendPath with no members (see also IS_DUMMY_PATH macro).
*/
static void
! set_dummy_rel_pathlist(RelOptInfo *rel)
{
/* Set dummy size estimates --- we leave attr_widths[] as zeroes */
rel->rows = 0;
rel->width = 0;
! add_path(rel, (Path *) create_append_path(rel, NIL));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
--- 444,456 ----
* AppendPath with no members (see also IS_DUMMY_PATH macro).
*/
static void
! set_dummy_rel_pathlist(RelOptInfo *rel, int options)
{
/* Set dummy size estimates --- we leave attr_widths[] as zeroes */
rel->rows = 0;
rel->width = 0;
! add_path(rel, (Path *) create_append_path(rel, NIL), options);
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
***************
*** 566,572 ****
pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys);
/* Generate appropriate path */
! add_path(rel, create_subqueryscan_path(rel, pathkeys));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
--- 579,585 ----
pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys);
/* Generate appropriate path */
! add_path(rel, create_subqueryscan_path(rel, pathkeys), root->glob->options);
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
***************
*** 583,589 ****
set_function_size_estimates(root, rel);
/* Generate appropriate path */
! add_path(rel, create_functionscan_path(root, rel));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
--- 596,602 ----
set_function_size_estimates(root, rel);
/* Generate appropriate path */
! add_path(rel, create_functionscan_path(root, rel), root->glob->options);
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
***************
*** 600,606 ****
set_values_size_estimates(root, rel);
/* Generate appropriate path */
! add_path(rel, create_valuesscan_path(root, rel));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
--- 613,619 ----
set_values_size_estimates(root, rel);
/* Generate appropriate path */
! add_path(rel, create_valuesscan_path(root, rel), root->glob->options);
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
***************
*** 759,764 ****
--- 772,787 ----
/* Find and save the cheapest paths for this rel */
set_cheapest(rel);
+ /* Add the RelOptInfo pointer rel to the
+ * root->rois list for later use if we have the
+ * planner output bit set
+ */
+ if (root->glob->options & CURSOR_OPT_EMIT_PLANNER_XML)
+ {
+ rel->id = makeStringInfo();
+ appendStringInfo(rel->id, "%s", print_relids_nd(rel->relids, root));
+ root->rois = lappend(root->rois, rel);
+ }
#ifdef OPTIMIZER_DEBUG
debug_print_rel(root, rel);
***************
*** 1102,1107 ****
--- 1125,1164 ----
}
}
+ /*
+ * This function is similar to the DEBUG function below, but this
+ * function must also be available in a non-DEBUG build in order
+ * to emit planner details.
+ */
+
+ static char*
+ print_relids_nd(Relids relids, PlannerInfo* root)
+ {
+ Relids tmprelids;
+ int x;
+ bool first = true;
+ char* relname;
+ StringInfo enstr;
+
+ enstr = makeStringInfo();
+
+ tmprelids = bms_copy(relids);
+ while ((x = bms_first_member(tmprelids)) >= 0)
+ {
+ if (!first)
+ appendStringInfo(enstr, "/");
+
+ /* Let's show the relation names */
+ relname = get_rel_name(root->simple_rte_array[x]->relid);
+ appendStringInfo(enstr, "%s", relname);
+
+ first = false;
+ }
+ bms_free(tmprelids);
+
+ return enstr->data;
+ }
+
/*****************************************************************************
* DEBUG SUPPORT
*****************************************************************************/
***************
*** 1109,1126 ****
#ifdef OPTIMIZER_DEBUG
static void
! print_relids(Relids relids)
{
Relids tmprelids;
int x;
bool first = true;
tmprelids = bms_copy(relids);
while ((x = bms_first_member(tmprelids)) >= 0)
{
if (!first)
! printf(" ");
! printf("%d", x);
first = false;
}
bms_free(tmprelids);
--- 1166,1187 ----
#ifdef OPTIMIZER_DEBUG
static void
! print_relids(Relids relids, PlannerInfo* root)
{
Relids tmprelids;
int x;
bool first = true;
+ char* relname;
tmprelids = bms_copy(relids);
while ((x = bms_first_member(tmprelids)) >= 0)
{
if (!first)
! printf("/");
!
! /* Let's show the relation names */
! relname = get_rel_name(root->simple_rte_array[x]->relid);
! printf("%s", relname);
first = false;
}
bms_free(tmprelids);
***************
*** 1207,1213 ****
if (path->parent)
{
printf("(");
! print_relids(path->parent->relids);
printf(") rows=%.0f", path->parent->rows);
}
printf(" cost=%.2f..%.2f\n", path->startup_cost, path->total_cost);
--- 1268,1274 ----
if (path->parent)
{
printf("(");
! print_relids(path->parent->relids, root);
printf(") rows=%.0f", path->parent->rows);
}
printf(" cost=%.2f..%.2f\n", path->startup_cost, path->total_cost);
***************
*** 1258,1264 ****
ListCell *l;
printf("RELOPTINFO (");
! print_relids(rel->relids);
printf("): rows=%.0f width=%d\n", rel->rows, rel->width);
if (rel->baserestrictinfo)
--- 1319,1326 ----
ListCell *l;
printf("RELOPTINFO (");
! print_relids(rel->relids, root);
!
printf("): rows=%.0f width=%d\n", rel->rows, rel->width);
if (rel->baserestrictinfo)
*** ./src/backend/optimizer/path/indxpath.c.orig 2008-09-21 12:39:14.000000000 -0700
--- ./src/backend/optimizer/path/indxpath.c 2008-09-21 12:40:56.000000000 -0700
***************
*** 181,187 ****
{
IndexPath *ipath = (IndexPath *) lfirst(l);
! add_path(rel, (Path *) ipath);
if (ipath->indexselectivity < 1.0 &&
!ScanDirectionIsBackward(ipath->indexscandir))
--- 181,187 ----
{
IndexPath *ipath = (IndexPath *) lfirst(l);
! add_path(rel, (Path *) ipath, root->glob->options);
if (ipath->indexselectivity < 1.0 &&
!ScanDirectionIsBackward(ipath->indexscandir))
***************
*** 217,223 ****
bitmapqual = choose_bitmap_and(root, rel, bitindexpaths, NULL);
bpath = create_bitmap_heap_path(root, rel, bitmapqual, NULL);
! add_path(rel, (Path *) bpath);
}
}
--- 217,223 ----
bitmapqual = choose_bitmap_and(root, rel, bitindexpaths, NULL);
bpath = create_bitmap_heap_path(root, rel, bitmapqual, NULL);
! add_path(rel, (Path *) bpath, root->glob->options);
}
}
*** ./src/backend/optimizer/path/joinpath.c.orig 2008-09-21 12:49:40.000000000 -0700
--- ./src/backend/optimizer/path/joinpath.c 2008-09-21 12:51:09.000000000 -0700
***************
*** 276,282 ****
merge_pathkeys,
cur_mergeclauses,
outerkeys,
! innerkeys));
}
}
--- 276,282 ----
merge_pathkeys,
cur_mergeclauses,
outerkeys,
! innerkeys), root->glob->options);
}
}
***************
*** 452,458 ****
outerpath,
inner_cheapest_total,
restrictlist,
! merge_pathkeys));
if (matpath != NULL)
add_path(joinrel, (Path *)
create_nestloop_path(root,
--- 452,458 ----
outerpath,
inner_cheapest_total,
restrictlist,
! merge_pathkeys), root->glob->options);
if (matpath != NULL)
add_path(joinrel, (Path *)
create_nestloop_path(root,
***************
*** 461,467 ****
outerpath,
matpath,
restrictlist,
! merge_pathkeys));
if (inner_cheapest_startup != inner_cheapest_total)
add_path(joinrel, (Path *)
create_nestloop_path(root,
--- 461,467 ----
outerpath,
matpath,
restrictlist,
! merge_pathkeys), root->glob->options);
if (inner_cheapest_startup != inner_cheapest_total)
add_path(joinrel, (Path *)
create_nestloop_path(root,
***************
*** 470,476 ****
outerpath,
inner_cheapest_startup,
restrictlist,
! merge_pathkeys));
if (index_cheapest_total != NULL)
add_path(joinrel, (Path *)
create_nestloop_path(root,
--- 470,476 ----
outerpath,
inner_cheapest_startup,
restrictlist,
! merge_pathkeys), root->glob->options);
if (index_cheapest_total != NULL)
add_path(joinrel, (Path *)
create_nestloop_path(root,
***************
*** 479,485 ****
outerpath,
index_cheapest_total,
restrictlist,
! merge_pathkeys));
if (index_cheapest_startup != NULL &&
index_cheapest_startup != index_cheapest_total)
add_path(joinrel, (Path *)
--- 479,485 ----
outerpath,
index_cheapest_total,
restrictlist,
! merge_pathkeys), root->glob->options);
if (index_cheapest_startup != NULL &&
index_cheapest_startup != index_cheapest_total)
add_path(joinrel, (Path *)
***************
*** 489,495 ****
outerpath,
index_cheapest_startup,
restrictlist,
! merge_pathkeys));
}
/* Can't do anything else if outer path needs to be unique'd */
--- 489,495 ----
outerpath,
index_cheapest_startup,
restrictlist,
! merge_pathkeys), root->glob->options);
}
/* Can't do anything else if outer path needs to be unique'd */
***************
*** 542,548 ****
merge_pathkeys,
mergeclauses,
NIL,
! innersortkeys));
/* Can't do anything else if inner path needs to be unique'd */
if (save_jointype == JOIN_UNIQUE_INNER)
--- 542,548 ----
merge_pathkeys,
mergeclauses,
NIL,
! innersortkeys), root->glob->options);
/* Can't do anything else if inner path needs to be unique'd */
if (save_jointype == JOIN_UNIQUE_INNER)
***************
*** 610,616 ****
merge_pathkeys,
newclauses,
NIL,
! NIL));
cheapest_total_inner = innerpath;
}
/* Same on the basis of cheapest startup cost ... */
--- 610,616 ----
merge_pathkeys,
newclauses,
NIL,
! NIL), root->glob->options);
cheapest_total_inner = innerpath;
}
/* Same on the basis of cheapest startup cost ... */
***************
*** 655,661 ****
merge_pathkeys,
newclauses,
NIL,
! NIL));
}
cheapest_startup_inner = innerpath;
}
--- 655,661 ----
merge_pathkeys,
newclauses,
NIL,
! NIL), root->glob->options);
}
cheapest_startup_inner = innerpath;
}
***************
*** 787,793 ****
cheapest_total_outer,
cheapest_total_inner,
restrictlist,
! hashclauses));
if (cheapest_startup_outer != cheapest_total_outer)
add_path(joinrel, (Path *)
create_hashjoin_path(root,
--- 787,793 ----
cheapest_total_outer,
cheapest_total_inner,
restrictlist,
! hashclauses), root->glob->options);
if (cheapest_startup_outer != cheapest_total_outer)
add_path(joinrel, (Path *)
create_hashjoin_path(root,
***************
*** 796,802 ****
cheapest_startup_outer,
cheapest_total_inner,
restrictlist,
! hashclauses));
}
}
--- 796,802 ----
cheapest_startup_outer,
cheapest_total_inner,
restrictlist,
! hashclauses), root->glob->options);
}
}
*** ./src/backend/optimizer/path/joinrels.c.orig 2008-09-21 12:41:20.000000000 -0700
--- ./src/backend/optimizer/path/joinrels.c 2008-09-21 12:42:33.000000000 -0700
***************
*** 28,34 ****
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
static bool is_dummy_rel(RelOptInfo *rel);
! static void mark_dummy_join(RelOptInfo *rel);
/*
--- 28,34 ----
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
static bool is_dummy_rel(RelOptInfo *rel);
! static void mark_dummy_join(RelOptInfo *rel, int options);
/*
***************
*** 595,601 ****
case JOIN_INNER:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER,
--- 595,601 ----
case JOIN_INNER:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel, root->glob->options);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER,
***************
*** 606,612 ****
case JOIN_LEFT:
if (is_dummy_rel(rel1))
{
! mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT,
--- 606,612 ----
case JOIN_LEFT:
if (is_dummy_rel(rel1))
{
! mark_dummy_join(joinrel, root->glob->options);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT,
***************
*** 617,623 ****
case JOIN_FULL:
if (is_dummy_rel(rel1) && is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL,
--- 617,623 ----
case JOIN_FULL:
if (is_dummy_rel(rel1) && is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel, root->glob->options);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL,
***************
*** 628,634 ****
case JOIN_RIGHT:
if (is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT,
--- 628,634 ----
case JOIN_RIGHT:
if (is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel, root->glob->options);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT,
***************
*** 639,645 ****
case JOIN_IN:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_IN,
--- 639,645 ----
case JOIN_IN:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel, root->glob->options);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_IN,
***************
*** 653,659 ****
case JOIN_REVERSE_IN:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel);
break;
}
/* REVERSE_IN isn't supported by joinpath.c */
--- 653,659 ----
case JOIN_REVERSE_IN:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel, root->glob->options);
break;
}
/* REVERSE_IN isn't supported by joinpath.c */
***************
*** 667,673 ****
case JOIN_UNIQUE_OUTER:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_OUTER,
--- 667,673 ----
case JOIN_UNIQUE_OUTER:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel, root->glob->options);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_OUTER,
***************
*** 678,684 ****
case JOIN_UNIQUE_INNER:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_INNER,
--- 678,684 ----
case JOIN_UNIQUE_INNER:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
{
! mark_dummy_join(joinrel, root->glob->options);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_INNER,
***************
*** 958,964 ****
* Mark a joinrel as proven empty.
*/
static void
! mark_dummy_join(RelOptInfo *rel)
{
/* Set dummy size estimate */
rel->rows = 0;
--- 958,964 ----
* Mark a joinrel as proven empty.
*/
static void
! mark_dummy_join(RelOptInfo *rel, int options)
{
/* Set dummy size estimate */
rel->rows = 0;
***************
*** 967,973 ****
rel->pathlist = NIL;
/* Set up the dummy path */
! add_path(rel, (Path *) create_append_path(rel, NIL));
/*
* Although set_cheapest will be done again later, we do it immediately
--- 967,973 ----
rel->pathlist = NIL;
/* Set up the dummy path */
! add_path(rel, (Path *) create_append_path(rel, NIL), options);
/*
* Although set_cheapest will be done again later, we do it immediately
*** ./src/backend/optimizer/path/tidpath.c.orig 2008-09-21 12:51:24.000000000 -0700
--- ./src/backend/optimizer/path/tidpath.c 2008-09-21 12:52:15.000000000 -0700
***************
*** 254,258 ****
tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
if (tidquals)
! add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals));
}
--- 254,258 ----
tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
if (tidquals)
! add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals), root->glob->options);
}
*** ./src/backend/optimizer/plan/createplan.c.orig 2008-09-21 12:33:39.000000000 -0700
--- ./src/backend/optimizer/plan/createplan.c 2008-09-21 12:36:52.000000000 -0700
***************
*** 187,192 ****
--- 187,197 ----
break;
}
+ /* Record the pointer to the path as a plan id. Helps to
+ * sort out where everything came from later */
+ plan->id = makeStringInfo();
+ appendStringInfo(plan->id, "%d", best_path);
+
return plan;
}
***************
*** 1784,1789 ****
--- 1789,1797 ----
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
+ ((Plan*)hash_plan)->id = makeStringInfo();
+ appendStringInfo(((Plan*)hash_plan)->id, "%d", hash_plan);
+
return join_plan;
}
*** ./src/backend/optimizer/plan/planner.c.orig 2008-09-21 12:37:07.000000000 -0700
--- ./src/backend/optimizer/plan/planner.c 2008-09-21 12:38:53.000000000 -0700
***************
*** 113,119 ****
PlannerInfo *root;
Plan *top_plan;
ListCell *lp,
! *lr;
/* Cursor options may come from caller or from DECLARE CURSOR stmt */
if (parse->utilityStmt &&
--- 113,119 ----
PlannerInfo *root;
Plan *top_plan;
ListCell *lp,
! *lr, *lcl;
/* Cursor options may come from caller or from DECLARE CURSOR stmt */
if (parse->utilityStmt &&
***************
*** 137,142 ****
--- 137,147 ----
glob->relationOids = NIL;
glob->transientPlan = false;
+ if (cursorOptions & CURSOR_OPT_EMIT_PLANNER_XML)
+ glob->options = CURSOR_OPT_EMIT_PLANNER_XML;
+ else
+ glob->options = 0;
+
/* Determine what fraction of the plan is likely to be scanned */
if (cursorOptions & CURSOR_OPT_FAST_PLAN)
{
***************
*** 182,187 ****
--- 187,252 ----
/* build the PlannedStmt result */
result = makeNode(PlannedStmt);
+ /*
+ * If the CURSOR_OPT_EMIT_PLANNER_XML bit has been set, then
+ * down in the guts of the planner we have stored a List of all
+ * RelOptInfo's used during the planning process in the
+ * PlannerInfo structure. And, when the planner in add_path
+ * attempted to reject (delete) a path under consideration,
+ * it was copied to the List rejectedlist instead of deleted.
+ *
+ * At this point, convert each path contained in either list
+ * to a Plan.
+ */
+
+ if (cursorOptions & CURSOR_OPT_EMIT_PLANNER_XML)
+ {
+
+ foreach(lcl, root->rois)
+ {
+ ListCell *pcl;
+ RelOptInfo *roi = (RelOptInfo*)lfirst(lcl);
+ Roi *proi = (Roi*) palloc0(sizeof(Roi));
+ /* Sequence through each path */
+ if (roi == NULL)
+ continue;
+
+ foreach (pcl, roi->pathlist)
+ {
+ Path *path = (Path*)lfirst(pcl);
+ Plan *plan;
+ if (path == NULL)
+ continue;
+
+ proi->id = roi->id;
+ plan = create_plan(root, path);
+ plan->pathkeys = print_pathkeys(path->pathkeys, root->parse->rtable);
+ plan->isdeleted = false;
+ proi->plans = lappend(proi->plans, plan);
+ }
+
+ /* And, iterate through the rejectedlist as well */
+ foreach (pcl, roi->rejectedlist)
+ {
+ Path *path = (Path*)lfirst(pcl);
+ Plan *plan;
+
+ if (path == NULL)
+ continue;
+
+ proi->id = roi->id;
+ plan = create_plan(root, path);
+ plan->pathkeys = print_pathkeys(path->pathkeys, root->parse->rtable);
+ plan->isdeleted = true;
+ proi->plans = lappend(proi->plans, plan);
+ }
+
+ result->roi_plans = lappend(result->roi_plans, proi);
+
+ }
+
+
+ }
result->commandType = parse->commandType;
result->canSetTag = parse->canSetTag;
*** ./src/backend/optimizer/util/pathnode.c.orig 2008-09-21 12:52:37.000000000 -0700
--- ./src/backend/optimizer/util/pathnode.c 2008-09-21 13:33:57.000000000 -0700
***************
*** 245,255 ****
*
* 'parent_rel' is the relation entry to which the path corresponds.
* 'new_path' is a potential path for parent_rel.
*
* Returns nothing, but modifies parent_rel->pathlist.
*/
void
! add_path(RelOptInfo *parent_rel, Path *new_path)
{
bool accept_new = true; /* unless we find a superior old path */
ListCell *insert_after = NULL; /* where to insert new item */
--- 245,258 ----
*
* 'parent_rel' is the relation entry to which the path corresponds.
* 'new_path' is a potential path for parent_rel.
+ * 'options' contains options, such as those from Explain. If options has the
+ * CURSOR_OPT_EMIT_PLANNER_XML bit set, then rejected paths are not
+ * pfree'd. They are instead copied to a list in the parent rel rejectedlist.
*
* Returns nothing, but modifies parent_rel->pathlist.
*/
void
! add_path(RelOptInfo *parent_rel, Path *new_path, int options)
{
bool accept_new = true; /* unless we find a superior old path */
ListCell *insert_after = NULL; /* where to insert new item */
***************
*** 338,347 ****
p1, p1_prev);
/*
! * Delete the data pointed-to by the deleted cell, if possible
*/
if (!IsA(old_path, IndexPath))
pfree(old_path);
/* Advance list pointer */
if (p1_prev)
p1 = lnext(p1_prev);
--- 341,360 ----
p1, p1_prev);
/*
! * Delete the data pointed-to by the deleted cell, if possible. But,
! * only do this is we don't need the nodes later for planner tracing.
*/
+
+ if (options & CURSOR_OPT_EMIT_PLANNER_XML)
+ {
+ parent_rel->rejectedlist = lappend(parent_rel->rejectedlist, old_path);
+ }
+ else
+ {
if (!IsA(old_path, IndexPath))
pfree(old_path);
+ }
+
/* Advance list pointer */
if (p1_prev)
p1 = lnext(p1_prev);
***************
*** 377,385 ****
}
else
{
! /* Reject and recycle the new path */
if (!IsA(new_path, IndexPath))
pfree(new_path);
}
}
--- 390,407 ----
}
else
{
! /* Reject and recycle the new path only if we don't need the nodes
! * later.
! */
! if (options & CURSOR_OPT_EMIT_PLANNER_XML)
! {
! parent_rel->rejectedlist = lappend(parent_rel->rejectedlist, new_path);
! }
! else
! {
if (!IsA(new_path, IndexPath))
pfree(new_path);
+ }
}
}
*** ./src/backend/optimizer/util/relnode.c.orig 2008-09-21 12:54:42.000000000 -0700
--- ./src/backend/optimizer/util/relnode.c 2008-09-21 12:55:50.000000000 -0700
***************
*** 72,77 ****
--- 72,78 ----
rel->width = 0;
rel->reltargetlist = NIL;
rel->pathlist = NIL;
+ rel->rejectedlist = NIL;
rel->cheapest_startup_path = NULL;
rel->cheapest_total_path = NULL;
rel->cheapest_unique_path = NULL;
***************
*** 321,326 ****
--- 322,328 ----
joinrel->width = 0;
joinrel->reltargetlist = NIL;
joinrel->pathlist = NIL;
+ joinrel->reltargetlist = NIL;
joinrel->cheapest_startup_path = NULL;
joinrel->cheapest_total_path = NULL;
joinrel->cheapest_unique_path = NULL;
*** ./src/backend/parser/gram.y.orig 2008-09-21 12:56:29.000000000 -0700
--- ./src/backend/parser/gram.y 2008-09-21 13:01:53.000000000 -0700
***************
*** 278,283 ****
--- 278,284 ----
%type opt_instead opt_analyze
%type index_opt_unique opt_verbose opt_full
%type opt_freeze opt_default opt_recheck
+ %type opt_xml
%type opt_binary opt_oids copy_delimiter
%type copy_from
***************
*** 5745,5756 ****
*
*****************************************************************************/
! ExplainStmt: EXPLAIN opt_analyze opt_verbose ExplainableStmt
{
ExplainStmt *n = makeNode(ExplainStmt);
n->analyze = $2;
n->verbose = $3;
! n->query = $4;
$$ = (Node *)n;
}
;
--- 5746,5758 ----
*
*****************************************************************************/
! ExplainStmt: EXPLAIN opt_analyze opt_verbose opt_xml ExplainableStmt
{
ExplainStmt *n = makeNode(ExplainStmt);
n->analyze = $2;
n->verbose = $3;
! n->xml = $4;
! n->query = $5;
$$ = (Node *)n;
}
;
***************
*** 5769,5774 ****
--- 5771,5781 ----
| /* EMPTY */ { $$ = FALSE; }
;
+ opt_xml:
+ XML_P { $$ = TRUE; }
+ | /* EMPTY */ { $$ = FALSE; }
+ ;
+
/*****************************************************************************
*
* QUERY:
*** ./src/bin/psql/sql_help.h.orig 2008-10-09 11:14:46.000000000 -0700
--- ./src/bin/psql/sql_help.h 2008-10-09 11:14:53.000000000 -0700
***************
*** 403,409 ****
{ "EXPLAIN",
N_("show the execution plan of a statement"),
! N_("EXPLAIN [ ANALYZE ] [ VERBOSE ] statement") },
{ "FETCH",
N_("retrieve rows from a query using a cursor"),
--- 403,409 ----
{ "EXPLAIN",
N_("show the execution plan of a statement"),
! N_("EXPLAIN [ ANALYZE ] [ VERBOSE ] [ XML ] statement") },
{ "FETCH",
N_("retrieve rows from a query using a cursor"),
*** ./src/include/nodes/parsenodes.h.orig 2008-09-21 12:25:52.000000000 -0700
--- ./src/include/nodes/parsenodes.h 2008-09-21 12:29:20.000000000 -0700
***************
*** 1482,1487 ****
--- 1482,1488 ----
#define CURSOR_OPT_INSENSITIVE 0x0008 /* INSENSITIVE */
#define CURSOR_OPT_HOLD 0x0010 /* WITH HOLD */
#define CURSOR_OPT_FAST_PLAN 0x0020 /* prefer fast-start plan */
+ #define CURSOR_OPT_EMIT_PLANNER_XML 0x0040 /* EMIT PLANNER DETAIL */
typedef struct DeclareCursorStmt
{
***************
*** 1870,1875 ****
--- 1871,1877 ----
Node *query; /* the query (as a raw parse tree) */
bool verbose; /* print plan info */
bool analyze; /* get statistics by executing plan */
+ bool xml; /* get the output as XML instead of plain text */
} ExplainStmt;
/* ----------------------
*** ./src/include/nodes/plannodes.h.orig 2008-09-21 12:31:25.000000000 -0700
--- ./src/include/nodes/plannodes.h 2008-09-21 12:32:35.000000000 -0700
***************
*** 17,22 ****
--- 17,23 ----
#include "access/sdir.h"
#include "nodes/bitmapset.h"
#include "nodes/primnodes.h"
+ #include "lib/stringinfo.h"
/* ----------------------------------------------------------------
***************
*** 24,29 ****
--- 25,38 ----
* ----------------------------------------------------------------
*/
+ /* Roi node */
+ typedef struct Roi
+ {
+ StringInfo id;
+ List *plans; /* All plans */
+
+ } Roi;
+
/* ----------------
* PlannedStmt node
*
***************
*** 42,47 ****
--- 51,57 ----
bool transientPlan; /* redo plan when TransactionXmin changes? */
struct Plan *planTree; /* tree of Plan nodes */
+ List *roi_plans; /* List of Roi nodes, each containing a list of plans */
List *rtable; /* list of RangeTblEntry nodes */
***************
*** 96,101 ****
--- 106,116 ----
{
NodeTag type;
+ bool isdeleted; /* useful for EXPLAIN XML */
+ char *pathkeys; /* String of path keys */
+
+ StringInfo id; /* Added to help identify where the Plan originated; */
+ /* Just a pointer to the Path node */
/*
* estimated execution costs for plan (see costsize.c for more info)
*/
*** ./src/include/nodes/print.h.orig 2008-09-21 12:24:04.000000000 -0700
--- ./src/include/nodes/print.h 2008-09-21 12:25:38.000000000 -0700
***************
*** 27,34 ****
extern char *format_node_dump(const char *dump);
extern char *pretty_format_node_dump(const char *dump);
extern void print_rt(List *rtable);
! extern void print_expr(Node *expr, List *rtable);
! extern void print_pathkeys(List *pathkeys, List *rtable);
extern void print_tl(List *tlist, List *rtable);
extern void print_slot(TupleTableSlot *slot);
--- 27,34 ----
extern char *format_node_dump(const char *dump);
extern char *pretty_format_node_dump(const char *dump);
extern void print_rt(List *rtable);
! extern char *print_expr(Node *expr, List *rtable);
! extern char *print_pathkeys(List *pathkeys, List *rtable);
extern void print_tl(List *tlist, List *rtable);
extern void print_slot(TupleTableSlot *slot);
*** ./src/include/nodes/relation.h.orig 2008-09-21 12:29:48.000000000 -0700
--- ./src/include/nodes/relation.h 2008-09-21 12:31:03.000000000 -0700
***************
*** 19,24 ****
--- 19,25 ----
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "storage/block.h"
+ #include "lib/stringinfo.h"
/*
***************
*** 75,80 ****
--- 76,82 ----
List *relationOids; /* OIDs of relations the plan depends on */
bool transientPlan; /* redo plan when TransactionXmin changes? */
+ int options; /* Added to support explain context */
} PlannerGlobal;
/* macro for fetching the Plan associated with a SubPlan node */
***************
*** 101,106 ****
--- 103,112 ----
PlannerGlobal *glob; /* global info for current planner run */
Index query_level; /* 1 at the outermost Query */
+ StringInfo xmlstr; /* Stores ROI output in XML form */
+
+ List *rois; /* We are storing each RelOptInfo structure here for use
+ * later when we convert each path contained there to a plan */
/*
* simple_rel_array holds pointers to "base rels" and "other rels" (see
***************
*** 320,325 ****
--- 326,333 ----
RelOptKind reloptkind;
+ StringInfo id; /* Added to make storage of the relid names a bit easier */
+
/* all relations included in this RelOptInfo */
Relids relids; /* set of base relids (rangetable indexes) */
***************
*** 330,335 ****
--- 338,345 ----
/* materialization information */
List *reltargetlist; /* needed Vars */
List *pathlist; /* Path structures */
+ List *rejectedlist; /* Rejected paths */
+
struct Path *cheapest_startup_path;
struct Path *cheapest_total_path;
struct Path *cheapest_unique_path;
*** ./src/include/optimizer/pathnode.h.orig 2008-01-01 11:45:58.000000000 -0800
--- ./src/include/optimizer/pathnode.h 2008-09-21 12:24:27.000000000 -0700
***************
*** 25,31 ****
extern int compare_fractional_path_costs(Path *path1, Path *path2,
double fraction);
extern void set_cheapest(RelOptInfo *parent_rel);
! extern void add_path(RelOptInfo *parent_rel, Path *new_path);
extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel);
extern IndexPath *create_index_path(PlannerInfo *root,
--- 25,31 ----
extern int compare_fractional_path_costs(Path *path1, Path *path2,
double fraction);
extern void set_cheapest(RelOptInfo *parent_rel);
! extern void add_path(RelOptInfo *parent_rel, Path *new_path, int options);
extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel);
extern IndexPath *create_index_path(PlannerInfo *root,