*** ./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,