#include "executor/executor.h"
#include "lib/stringinfo.h"
Go to the source code of this file.
typedef const char*(* explain_get_index_name_hook_type)(Oid indexId) |
typedef enum ExplainFormat ExplainFormat |
typedef void(* ExplainOneQuery_hook_type)(Query *query, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params) |
typedef struct ExplainState ExplainState |
enum ExplainFormat |
Definition at line 19 of file explain.h.
{ EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_YAML } ExplainFormat;
void ExplainBeginOutput | ( | ExplainState * | es | ) |
Definition at line 2449 of file explain.c.
References appendStringInfoChar(), appendStringInfoString(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainState::format, ExplainState::grouping_stack, ExplainState::indent, lcons_int(), and ExplainState::str.
Referenced by explain_ExecutorEnd(), and ExplainQuery().
{ switch (es->format) { case EXPLAIN_FORMAT_TEXT: /* nothing to do */ break; case EXPLAIN_FORMAT_XML: appendStringInfoString(es->str, "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n"); es->indent++; break; case EXPLAIN_FORMAT_JSON: /* top-level structure is an array of plans */ appendStringInfoChar(es->str, '['); es->grouping_stack = lcons_int(0, es->grouping_stack); es->indent++; break; case EXPLAIN_FORMAT_YAML: es->grouping_stack = lcons_int(0, es->grouping_stack); break; } }
void ExplainEndOutput | ( | ExplainState * | es | ) |
Definition at line 2480 of file explain.c.
References appendStringInfoString(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainState::format, ExplainState::grouping_stack, ExplainState::indent, list_delete_first(), and ExplainState::str.
Referenced by explain_ExecutorEnd(), and ExplainQuery().
{ switch (es->format) { case EXPLAIN_FORMAT_TEXT: /* nothing to do */ break; case EXPLAIN_FORMAT_XML: es->indent--; appendStringInfoString(es->str, "</explain>"); break; case EXPLAIN_FORMAT_JSON: es->indent--; appendStringInfoString(es->str, "\n]"); es->grouping_stack = list_delete_first(es->grouping_stack); break; case EXPLAIN_FORMAT_YAML: es->grouping_stack = list_delete_first(es->grouping_stack); break; } }
void ExplainInitState | ( | ExplainState * | es | ) |
Definition at line 249 of file explain.c.
References ExplainState::costs, makeStringInfo(), and ExplainState::str.
Referenced by explain_ExecutorEnd(), and ExplainQuery().
{ /* Set default options. */ memset(es, 0, sizeof(ExplainState)); es->costs = true; /* Prepare output buffer. */ es->str = makeStringInfo(); }
void ExplainOnePlan | ( | PlannedStmt * | plannedstmt, | |
IntoClause * | into, | |||
ExplainState * | es, | |||
const char * | queryString, | |||
ParamListInfo | params | |||
) |
Definition at line 399 of file explain.c.
References ExplainState::analyze, appendStringInfo(), ExplainState::buffers, CommandCounterIncrement(), CreateIntoRelDestReceiver(), CreateQueryDesc(), elapsed_time(), EState::es_num_result_relations, EState::es_result_relations, EState::es_trig_target_relations, QueryDesc::estate, ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPrintPlan(), ExplainPropertyFloat(), ExplainState::format, FreeQueryDesc(), GetActiveSnapshot(), GetIntoRelEFlags(), INSTR_TIME_SET_CURRENT, InvalidSnapshot, lfirst, None_Receiver, NULL, PopActiveSnapshot(), PushCopiedSnapshot(), report_triggers(), IntoClause::skipData, ExplainState::str, ExplainState::timing, and UpdateActiveSnapshotCommandId().
Referenced by ExplainExecuteQuery(), and ExplainOneQuery().
{ DestReceiver *dest; QueryDesc *queryDesc; instr_time starttime; double totaltime = 0; int eflags; int instrument_option = 0; if (es->analyze && es->timing) instrument_option |= INSTRUMENT_TIMER; else if (es->analyze) instrument_option |= INSTRUMENT_ROWS; if (es->buffers) instrument_option |= INSTRUMENT_BUFFERS; INSTR_TIME_SET_CURRENT(starttime); /* * Use a snapshot with an updated command ID to ensure this query sees * results of any previously executed queries. */ PushCopiedSnapshot(GetActiveSnapshot()); UpdateActiveSnapshotCommandId(); /* * Normally we discard the query's output, but if explaining CREATE TABLE * AS, we'd better use the appropriate tuple receiver. */ if (into) dest = CreateIntoRelDestReceiver(into); else dest = None_Receiver; /* Create a QueryDesc for the query */ queryDesc = CreateQueryDesc(plannedstmt, queryString, GetActiveSnapshot(), InvalidSnapshot, dest, params, instrument_option); /* Select execution options */ if (es->analyze) eflags = 0; /* default run-to-completion flags */ else eflags = EXEC_FLAG_EXPLAIN_ONLY; if (into) eflags |= GetIntoRelEFlags(into); /* call ExecutorStart to prepare the plan for execution */ ExecutorStart(queryDesc, eflags); /* Execute the plan for statistics if asked for */ if (es->analyze) { ScanDirection dir; /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */ if (into && into->skipData) dir = NoMovementScanDirection; else dir = ForwardScanDirection; /* run the plan */ ExecutorRun(queryDesc, dir, 0L); /* run cleanup too */ ExecutorFinish(queryDesc); /* We can't run ExecutorEnd 'till we're done printing the stats... */ totaltime += elapsed_time(&starttime); } ExplainOpenGroup("Query", NULL, true, es); /* Create textual dump of plan tree */ ExplainPrintPlan(es, queryDesc); /* Print info about runtime of triggers */ if (es->analyze) { ResultRelInfo *rInfo; bool show_relname; int numrels = queryDesc->estate->es_num_result_relations; List *targrels = queryDesc->estate->es_trig_target_relations; int nr; ListCell *l; ExplainOpenGroup("Triggers", "Triggers", false, es); show_relname = (numrels > 1 || targrels != NIL); rInfo = queryDesc->estate->es_result_relations; for (nr = 0; nr < numrels; rInfo++, nr++) report_triggers(rInfo, show_relname, es); foreach(l, targrels) { rInfo = (ResultRelInfo *) lfirst(l); report_triggers(rInfo, show_relname, es); } ExplainCloseGroup("Triggers", "Triggers", false, es); } /* * Close down the query and free resources. Include time for this in the * total runtime (although it should be pretty minimal). */ INSTR_TIME_SET_CURRENT(starttime); ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); PopActiveSnapshot(); /* We need a CCI just in case query expanded to multiple plans */ if (es->analyze) CommandCounterIncrement(); totaltime += elapsed_time(&starttime); if (es->analyze) { if (es->format == EXPLAIN_FORMAT_TEXT) appendStringInfo(es->str, "Total runtime: %.3f ms\n", 1000.0 * totaltime); else ExplainPropertyFloat("Total Runtime", 1000.0 * totaltime, 3, es); } ExplainCloseGroup("Query", NULL, true, es); }
void ExplainOneUtility | ( | Node * | utilityStmt, | |
IntoClause * | into, | |||
ExplainState * | es, | |||
const char * | queryString, | |||
ParamListInfo | params | |||
) |
Definition at line 339 of file explain.c.
References appendStringInfoString(), Assert, copyObject(), EXPLAIN_FORMAT_TEXT, ExplainDummyGroup(), ExplainExecuteQuery(), ExplainOneQuery(), ExplainState::format, CreateTableAsStmt::into, IsA, linitial, list_length(), NULL, CreateTableAsStmt::query, QueryRewrite(), and ExplainState::str.
Referenced by ExplainExecuteQuery(), and ExplainOneQuery().
{ if (utilityStmt == NULL) return; if (IsA(utilityStmt, CreateTableAsStmt)) { /* * We have to rewrite the contained SELECT and then pass it back to * ExplainOneQuery. It's probably not really necessary to copy the * contained parsetree another time, but let's be safe. */ CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt; List *rewritten; Assert(IsA(ctas->query, Query)); rewritten = QueryRewrite((Query *) copyObject(ctas->query)); Assert(list_length(rewritten) == 1); ExplainOneQuery((Query *) linitial(rewritten), ctas->into, es, queryString, params); } else if (IsA(utilityStmt, ExecuteStmt)) ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es, queryString, params); else if (IsA(utilityStmt, NotifyStmt)) { if (es->format == EXPLAIN_FORMAT_TEXT) appendStringInfoString(es->str, "NOTIFY\n"); else ExplainDummyGroup("Notify", NULL, es); } else { if (es->format == EXPLAIN_FORMAT_TEXT) appendStringInfoString(es->str, "Utility statements have no plan structure\n"); else ExplainDummyGroup("Utility Statement", NULL, es); } }
void ExplainPrintPlan | ( | ExplainState * | es, | |
QueryDesc * | queryDesc | |||
) |
Definition at line 545 of file explain.c.
References Assert, ExplainNode(), ExplainPreScanNode(), NIL, NULL, QueryDesc::plannedstmt, QueryDesc::planstate, ExplainState::pstmt, PlannedStmt::rtable, ExplainState::rtable, ExplainState::rtable_names, and select_rtable_names_for_explain().
Referenced by explain_ExecutorEnd(), and ExplainOnePlan().
{ Bitmapset *rels_used = NULL; Assert(queryDesc->plannedstmt != NULL); es->pstmt = queryDesc->plannedstmt; es->rtable = queryDesc->plannedstmt->rtable; ExplainPreScanNode(queryDesc->planstate, &rels_used); es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used); ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es); }
void ExplainPropertyFloat | ( | const char * | qlabel, | |
double | value, | |||
int | ndigits, | |||
ExplainState * | es | |||
) |
Definition at line 2285 of file explain.c.
References buf, ExplainProperty(), and snprintf().
Referenced by ExplainNode(), ExplainOnePlan(), report_triggers(), and show_instrumentation_count().
{ char buf[256]; snprintf(buf, sizeof(buf), "%.*f", ndigits, value); ExplainProperty(qlabel, buf, true, es); }
void ExplainPropertyInteger | ( | const char * | qlabel, | |
int | value, | |||
ExplainState * | es | |||
) |
Definition at line 2260 of file explain.c.
References buf, ExplainProperty(), and snprintf().
Referenced by ExplainNode().
{ char buf[32]; snprintf(buf, sizeof(buf), "%d", value); ExplainProperty(qlabel, buf, true, es); }
void ExplainPropertyList | ( | const char * | qlabel, | |
List * | data, | |||
ExplainState * | es | |||
) |
Definition at line 2126 of file explain.c.
References appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), escape_json(), escape_xml(), escape_yaml(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainJSONLineEnding(), ExplainXMLTag(), ExplainYAMLLineStarting(), ExplainState::format, ExplainState::indent, lfirst, pfree(), ExplainState::str, X_CLOSING, and X_OPENING.
Referenced by show_plan_tlist(), and show_sort_keys_common().
{ ListCell *lc; bool first = true; switch (es->format) { case EXPLAIN_FORMAT_TEXT: appendStringInfoSpaces(es->str, es->indent * 2); appendStringInfo(es->str, "%s: ", qlabel); foreach(lc, data) { if (!first) appendStringInfoString(es->str, ", "); appendStringInfoString(es->str, (const char *) lfirst(lc)); first = false; } appendStringInfoChar(es->str, '\n'); break; case EXPLAIN_FORMAT_XML: ExplainXMLTag(qlabel, X_OPENING, es); foreach(lc, data) { char *str; appendStringInfoSpaces(es->str, es->indent * 2 + 2); appendStringInfoString(es->str, "<Item>"); str = escape_xml((const char *) lfirst(lc)); appendStringInfoString(es->str, str); pfree(str); appendStringInfoString(es->str, "</Item>\n"); } ExplainXMLTag(qlabel, X_CLOSING, es); break; case EXPLAIN_FORMAT_JSON: ExplainJSONLineEnding(es); appendStringInfoSpaces(es->str, es->indent * 2); escape_json(es->str, qlabel); appendStringInfoString(es->str, ": ["); foreach(lc, data) { if (!first) appendStringInfoString(es->str, ", "); escape_json(es->str, (const char *) lfirst(lc)); first = false; } appendStringInfoChar(es->str, ']'); break; case EXPLAIN_FORMAT_YAML: ExplainYAMLLineStarting(es); appendStringInfo(es->str, "%s: ", qlabel); foreach(lc, data) { appendStringInfoChar(es->str, '\n'); appendStringInfoSpaces(es->str, es->indent * 2 + 2); appendStringInfoString(es->str, "- "); escape_yaml(es->str, (const char *) lfirst(lc)); } break; } }
void ExplainPropertyLong | ( | const char * | qlabel, | |
long | value, | |||
ExplainState * | es | |||
) |
Definition at line 2272 of file explain.c.
References buf, ExplainProperty(), and snprintf().
Referenced by ExplainNode(), fileExplainForeignScan(), show_hash_info(), and show_sort_info().
{ char buf[32]; snprintf(buf, sizeof(buf), "%ld", value); ExplainProperty(qlabel, buf, true, es); }
void ExplainPropertyText | ( | const char * | qlabel, | |
const char * | value, | |||
ExplainState * | es | |||
) |
Definition at line 2251 of file explain.c.
References ExplainProperty().
Referenced by ExplainIndexScanDetails(), ExplainNode(), ExplainQueryText(), ExplainTargetRel(), fileExplainForeignScan(), postgresExplainForeignModify(), postgresExplainForeignScan(), report_triggers(), show_expression(), and show_sort_info().
{ ExplainProperty(qlabel, value, false, es); }
void ExplainQuery | ( | ExplainStmt * | stmt, | |
const char * | queryString, | |||
ParamListInfo | params, | |||
DestReceiver * | dest | |||
) |
Definition at line 118 of file explain.c.
References ExplainState::analyze, appendStringInfoString(), Assert, begin_tup_output_tupdesc(), ExplainState::buffers, copyObject(), ExplainState::costs, StringInfoData::data, defGetBoolean(), defGetString(), DefElem::defname, do_text_output_multiline(), do_text_output_oneline, end_tup_output(), ereport, errcode(), errmsg(), ERROR, EXPLAIN_FORMAT_TEXT, ExplainBeginOutput(), ExplainEndOutput(), ExplainInitState(), ExplainOneQuery(), ExplainResultDesc(), ExplainSeparatePlans(), ExplainState::format, ExplainState::indent, IsA, lfirst, lnext, NIL, NULL, ExplainStmt::options, pfree(), ExplainStmt::query, QueryRewrite(), ExplainState::str, ExplainState::timing, and ExplainState::verbose.
Referenced by standard_ProcessUtility().
{ ExplainState es; TupOutputState *tstate; List *rewritten; ListCell *lc; bool timing_set = false; /* Initialize ExplainState. */ ExplainInitState(&es); /* Parse options list. */ foreach(lc, stmt->options) { DefElem *opt = (DefElem *) lfirst(lc); if (strcmp(opt->defname, "analyze") == 0) es.analyze = defGetBoolean(opt); else if (strcmp(opt->defname, "verbose") == 0) es.verbose = defGetBoolean(opt); else if (strcmp(opt->defname, "costs") == 0) es.costs = defGetBoolean(opt); else if (strcmp(opt->defname, "buffers") == 0) es.buffers = defGetBoolean(opt); else if (strcmp(opt->defname, "timing") == 0) { timing_set = true; es.timing = defGetBoolean(opt); } else if (strcmp(opt->defname, "format") == 0) { char *p = defGetString(opt); if (strcmp(p, "text") == 0) es.format = EXPLAIN_FORMAT_TEXT; else if (strcmp(p, "xml") == 0) es.format = EXPLAIN_FORMAT_XML; else if (strcmp(p, "json") == 0) es.format = EXPLAIN_FORMAT_JSON; else if (strcmp(p, "yaml") == 0) es.format = EXPLAIN_FORMAT_YAML; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"", opt->defname, p))); } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized EXPLAIN option \"%s\"", opt->defname))); } if (es.buffers && !es.analyze) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("EXPLAIN option BUFFERS requires ANALYZE"))); /* if the timing was not set explicitly, set default value */ es.timing = (timing_set) ? es.timing : es.analyze; /* check that timing is used with EXPLAIN ANALYZE */ if (es.timing && !es.analyze) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("EXPLAIN option TIMING requires ANALYZE"))); /* * Parse analysis was done already, but we still have to run the rule * rewriter. We do not do AcquireRewriteLocks: we assume the query either * came straight from the parser, or suitable locks were acquired by * plancache.c. * * Because the rewriter and planner tend to scribble on the input, we make * a preliminary copy of the source querytree. This prevents problems in * the case that the EXPLAIN is in a portal or plpgsql function and is * executed repeatedly. (See also the same hack in DECLARE CURSOR and * PREPARE.) XXX FIXME someday. */ Assert(IsA(stmt->query, Query)); rewritten = QueryRewrite((Query *) copyObject(stmt->query)); /* emit opening boilerplate */ ExplainBeginOutput(&es); if (rewritten == NIL) { /* * In the case of an INSTEAD NOTHING, tell at least that. But in * non-text format, the output is delimited, so this isn't necessary. */ if (es.format == EXPLAIN_FORMAT_TEXT) appendStringInfoString(es.str, "Query rewrites to nothing\n"); } else { ListCell *l; /* Explain every plan */ foreach(l, rewritten) { ExplainOneQuery((Query *) lfirst(l), NULL, &es, queryString, params); /* Separate plans with an appropriate separator */ if (lnext(l) != NULL) ExplainSeparatePlans(&es); } } /* emit closing boilerplate */ ExplainEndOutput(&es); Assert(es.indent == 0); /* output tuples */ tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt)); if (es.format == EXPLAIN_FORMAT_TEXT) do_text_output_multiline(tstate, es.str->data); else do_text_output_oneline(tstate, es.str->data); end_tup_output(tstate); pfree(es.str->data); }
void ExplainQueryText | ( | ExplainState * | es, | |
QueryDesc * | queryDesc | |||
) |
Definition at line 566 of file explain.c.
References ExplainPropertyText(), and QueryDesc::sourceText.
Referenced by explain_ExecutorEnd().
{ if (queryDesc->sourceText) ExplainPropertyText("Query Text", queryDesc->sourceText, es); }
TupleDesc ExplainResultDesc | ( | ExplainStmt * | stmt | ) |
Definition at line 263 of file explain.c.
References CreateTemplateTupleDesc(), defGetString(), DefElem::defname, lfirst, ExplainStmt::options, and TupleDescInitEntry().
Referenced by ExplainQuery(), and UtilityTupleDescriptor().
{ TupleDesc tupdesc; ListCell *lc; Oid result_type = TEXTOID; /* Check for XML format option */ foreach(lc, stmt->options) { DefElem *opt = (DefElem *) lfirst(lc); if (strcmp(opt->defname, "format") == 0) { char *p = defGetString(opt); if (strcmp(p, "xml") == 0) result_type = XMLOID; else if (strcmp(p, "json") == 0) result_type = JSONOID; else result_type = TEXTOID; /* don't "break", as ExplainQuery will use the last value */ } } /* Need a tuple descriptor representing a single TEXT or XML column */ tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN", result_type, -1, 0); return tupdesc; }
void ExplainSeparatePlans | ( | ExplainState * | es | ) |
Definition at line 2509 of file explain.c.
References appendStringInfoChar(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainState::format, and ExplainState::str.
Referenced by ExplainExecuteQuery(), and ExplainQuery().
{ switch (es->format) { case EXPLAIN_FORMAT_TEXT: /* add a blank line */ appendStringInfoChar(es->str, '\n'); break; case EXPLAIN_FORMAT_XML: case EXPLAIN_FORMAT_JSON: case EXPLAIN_FORMAT_YAML: /* nothing to do */ break; } }
Definition at line 40 of file explain.c.
Referenced by explain_get_index_name().
PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook |
Definition at line 37 of file explain.c.
Referenced by ExplainOneQuery().