#include "postgres.h"#include "access/xact.h"#include "catalog/pg_type.h"#include "commands/createas.h"#include "commands/defrem.h"#include "commands/prepare.h"#include "executor/hashjoin.h"#include "foreign/fdwapi.h"#include "optimizer/clauses.h"#include "parser/parsetree.h"#include "rewrite/rewriteHandler.h"#include "tcop/tcopprot.h"#include "utils/builtins.h"#include "utils/json.h"#include "utils/lsyscache.h"#include "utils/rel.h"#include "utils/snapmgr.h"#include "utils/tuplesort.h"#include "utils/xml.h"
Go to the source code of this file.
Defines | |
| #define | X_OPENING 0 |
| #define | X_CLOSING 1 |
| #define | X_CLOSE_IMMEDIATE 2 |
| #define | X_NOWHITESPACE 4 |
Functions | |
| static void | ExplainOneQuery (Query *query, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params) |
| static void | report_triggers (ResultRelInfo *rInfo, bool show_relname, ExplainState *es) |
| static double | elapsed_time (instr_time *starttime) |
| static void | ExplainPreScanNode (PlanState *planstate, Bitmapset **rels_used) |
| static void | ExplainPreScanMemberNodes (List *plans, PlanState **planstates, Bitmapset **rels_used) |
| static void | ExplainPreScanSubPlans (List *plans, Bitmapset **rels_used) |
| static void | ExplainNode (PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es) |
| static void | show_plan_tlist (PlanState *planstate, List *ancestors, ExplainState *es) |
| static void | show_expression (Node *node, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es) |
| static void | show_qual (List *qual, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es) |
| static void | show_scan_qual (List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es) |
| static void | show_upper_qual (List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es) |
| static void | show_sort_keys (SortState *sortstate, List *ancestors, ExplainState *es) |
| static void | show_merge_append_keys (MergeAppendState *mstate, List *ancestors, ExplainState *es) |
| static void | show_sort_keys_common (PlanState *planstate, int nkeys, AttrNumber *keycols, List *ancestors, ExplainState *es) |
| static void | show_sort_info (SortState *sortstate, ExplainState *es) |
| static void | show_hash_info (HashState *hashstate, ExplainState *es) |
| static void | show_instrumentation_count (const char *qlabel, int which, PlanState *planstate, ExplainState *es) |
| static void | show_foreignscan_info (ForeignScanState *fsstate, ExplainState *es) |
| static const char * | explain_get_index_name (Oid indexId) |
| static void | ExplainIndexScanDetails (Oid indexid, ScanDirection indexorderdir, ExplainState *es) |
| static void | ExplainScanTarget (Scan *plan, ExplainState *es) |
| static void | ExplainModifyTarget (ModifyTable *plan, ExplainState *es) |
| static void | ExplainTargetRel (Plan *plan, Index rti, ExplainState *es) |
| static void | show_modifytable_info (ModifyTableState *mtstate, ExplainState *es) |
| static void | ExplainMemberNodes (List *plans, PlanState **planstates, List *ancestors, ExplainState *es) |
| static void | ExplainSubPlans (List *plans, List *ancestors, const char *relationship, ExplainState *es) |
| static void | ExplainProperty (const char *qlabel, const char *value, bool numeric, ExplainState *es) |
| static void | ExplainOpenGroup (const char *objtype, const char *labelname, bool labeled, ExplainState *es) |
| static void | ExplainCloseGroup (const char *objtype, const char *labelname, bool labeled, ExplainState *es) |
| static void | ExplainDummyGroup (const char *objtype, const char *labelname, ExplainState *es) |
| static void | ExplainXMLTag (const char *tagname, int flags, ExplainState *es) |
| static void | ExplainJSONLineEnding (ExplainState *es) |
| static void | ExplainYAMLLineStarting (ExplainState *es) |
| static void | escape_yaml (StringInfo buf, const char *str) |
| void | ExplainQuery (ExplainStmt *stmt, const char *queryString, ParamListInfo params, DestReceiver *dest) |
| void | ExplainInitState (ExplainState *es) |
| TupleDesc | ExplainResultDesc (ExplainStmt *stmt) |
| void | ExplainOneUtility (Node *utilityStmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params) |
| void | ExplainOnePlan (PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params) |
| void | ExplainPrintPlan (ExplainState *es, QueryDesc *queryDesc) |
| void | ExplainQueryText (ExplainState *es, QueryDesc *queryDesc) |
| void | ExplainPropertyList (const char *qlabel, List *data, ExplainState *es) |
| void | ExplainPropertyText (const char *qlabel, const char *value, ExplainState *es) |
| void | ExplainPropertyInteger (const char *qlabel, int value, ExplainState *es) |
| void | ExplainPropertyLong (const char *qlabel, long value, ExplainState *es) |
| void | ExplainPropertyFloat (const char *qlabel, double value, int ndigits, ExplainState *es) |
| void | ExplainBeginOutput (ExplainState *es) |
| void | ExplainEndOutput (ExplainState *es) |
| void | ExplainSeparatePlans (ExplainState *es) |
Variables | |
| ExplainOneQuery_hook_type | ExplainOneQuery_hook = NULL |
| explain_get_index_name_hook_type | explain_get_index_name_hook = NULL |
| #define X_CLOSE_IMMEDIATE 2 |
Definition at line 46 of file explain.c.
Referenced by ExplainDummyGroup(), and ExplainXMLTag().
| #define X_CLOSING 1 |
Definition at line 45 of file explain.c.
Referenced by ExplainCloseGroup(), ExplainProperty(), ExplainPropertyList(), and ExplainXMLTag().
| #define X_NOWHITESPACE 4 |
Definition at line 47 of file explain.c.
Referenced by ExplainProperty(), and ExplainXMLTag().
| #define X_OPENING 0 |
Definition at line 44 of file explain.c.
Referenced by ExplainOpenGroup(), ExplainProperty(), and ExplainPropertyList().
| static double elapsed_time | ( | instr_time * | starttime | ) | [static] |
Definition at line 643 of file explain.c.
References INSTR_TIME_GET_DOUBLE, INSTR_TIME_SET_CURRENT, and INSTR_TIME_SUBTRACT.
Referenced by ExplainOnePlan().
{
instr_time endtime;
INSTR_TIME_SET_CURRENT(endtime);
INSTR_TIME_SUBTRACT(endtime, *starttime);
return INSTR_TIME_GET_DOUBLE(endtime);
}
| static void escape_yaml | ( | StringInfo | buf, | |
| const char * | str | |||
| ) | [static] |
Definition at line 2608 of file explain.c.
References escape_json().
Referenced by ExplainDummyGroup(), ExplainProperty(), and ExplainPropertyList().
{
escape_json(buf, str);
}
| static const char * explain_get_index_name | ( | Oid | indexId | ) | [static] |
Definition at line 1863 of file explain.c.
References elog, ERROR, explain_get_index_name_hook, get_rel_name(), NULL, and quote_identifier().
Referenced by ExplainIndexScanDetails(), and ExplainNode().
{
const char *result;
if (explain_get_index_name_hook)
result = (*explain_get_index_name_hook) (indexId);
else
result = NULL;
if (result == NULL)
{
/* default behavior: look in the catalogs and quote it */
result = get_rel_name(indexId);
if (result == NULL)
elog(ERROR, "cache lookup failed for index %u", indexId);
result = quote_identifier(result);
}
return result;
}
| 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;
}
}
| static void ExplainCloseGroup | ( | const char * | objtype, | |
| const char * | labelname, | |||
| bool | labeled, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 2367 of file explain.c.
References appendStringInfoChar(), appendStringInfoSpaces(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainXMLTag(), ExplainState::format, ExplainState::grouping_stack, ExplainState::indent, list_delete_first(), ExplainState::str, and X_CLOSING.
Referenced by ExplainNode(), ExplainOnePlan(), and report_triggers().
{
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
/* nothing to do */
break;
case EXPLAIN_FORMAT_XML:
es->indent--;
ExplainXMLTag(objtype, X_CLOSING, es);
break;
case EXPLAIN_FORMAT_JSON:
es->indent--;
appendStringInfoChar(es->str, '\n');
appendStringInfoSpaces(es->str, 2 * es->indent);
appendStringInfoChar(es->str, labeled ? '}' : ']');
es->grouping_stack = list_delete_first(es->grouping_stack);
break;
case EXPLAIN_FORMAT_YAML:
es->indent--;
es->grouping_stack = list_delete_first(es->grouping_stack);
break;
}
}
| static void ExplainDummyGroup | ( | const char * | objtype, | |
| const char * | labelname, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 2403 of file explain.c.
References appendStringInfoSpaces(), appendStringInfoString(), escape_json(), escape_yaml(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainJSONLineEnding(), ExplainXMLTag(), ExplainYAMLLineStarting(), ExplainState::format, ExplainState::indent, ExplainState::str, and X_CLOSE_IMMEDIATE.
Referenced by ExplainOneUtility().
{
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
/* nothing to do */
break;
case EXPLAIN_FORMAT_XML:
ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
break;
case EXPLAIN_FORMAT_JSON:
ExplainJSONLineEnding(es);
appendStringInfoSpaces(es->str, 2 * es->indent);
if (labelname)
{
escape_json(es->str, labelname);
appendStringInfoString(es->str, ": ");
}
escape_json(es->str, objtype);
break;
case EXPLAIN_FORMAT_YAML:
ExplainYAMLLineStarting(es);
if (labelname)
{
escape_yaml(es->str, labelname);
appendStringInfoString(es->str, ": ");
}
else
{
appendStringInfoString(es->str, "- ");
}
escape_yaml(es->str, objtype);
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;
}
}
| static void ExplainIndexScanDetails | ( | Oid | indexid, | |
| ScanDirection | indexorderdir, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1886 of file explain.c.
References appendStringInfo(), appendStringInfoString(), BackwardScanDirection, EXPLAIN_FORMAT_TEXT, explain_get_index_name(), ExplainPropertyText(), ExplainState::format, ForwardScanDirection, NoMovementScanDirection, ScanDirectionIsBackward, and ExplainState::str.
Referenced by ExplainNode().
{
const char *indexname = explain_get_index_name(indexid);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
if (ScanDirectionIsBackward(indexorderdir))
appendStringInfoString(es->str, " Backward");
appendStringInfo(es->str, " using %s", indexname);
}
else
{
const char *scandir;
switch (indexorderdir)
{
case BackwardScanDirection:
scandir = "Backward";
break;
case NoMovementScanDirection:
scandir = "NoMovement";
break;
case ForwardScanDirection:
scandir = "Forward";
break;
default:
scandir = "???";
break;
}
ExplainPropertyText("Scan Direction", scandir, es);
ExplainPropertyText("Index Name", indexname, es);
}
}
| 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();
}
| static void ExplainJSONLineEnding | ( | ExplainState * | es | ) | [static] |
Definition at line 2563 of file explain.c.
References appendStringInfoChar(), Assert, EXPLAIN_FORMAT_JSON, ExplainState::format, ExplainState::grouping_stack, linitial_int, and ExplainState::str.
Referenced by ExplainDummyGroup(), ExplainOpenGroup(), ExplainProperty(), and ExplainPropertyList().
{
Assert(es->format == EXPLAIN_FORMAT_JSON);
if (linitial_int(es->grouping_stack) != 0)
appendStringInfoChar(es->str, ',');
else
linitial_int(es->grouping_stack) = 1;
appendStringInfoChar(es->str, '\n');
}
| static void ExplainMemberNodes | ( | List * | plans, | |
| PlanState ** | planstates, | |||
| List * | ancestors, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 2088 of file explain.c.
References ExplainNode(), list_length(), and NULL.
Referenced by ExplainNode().
{
int nplans = list_length(plans);
int j;
for (j = 0; j < nplans; j++)
ExplainNode(planstates[j], ancestors,
"Member", NULL, es);
}
| static void ExplainModifyTarget | ( | ModifyTable * | plan, | |
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1934 of file explain.c.
References Assert, ExplainTargetRel(), linitial_int, NIL, and ModifyTable::resultRelations.
Referenced by ExplainNode().
{
Index rti;
/*
* We show the name of the first target relation. In multi-target-table
* cases this should always be the parent of the inheritance tree.
*/
Assert(plan->resultRelations != NIL);
rti = linitial_int(plan->resultRelations);
ExplainTargetRel((Plan *) plan, rti, es);
}
| static void ExplainNode | ( | PlanState * | planstate, | |
| List * | ancestors, | |||
| const char * | relationship, | |||
| const char * | plan_name, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 799 of file explain.c.
References AGG_HASHED, AGG_PLAIN, AGG_SORTED, ExplainState::analyze, appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), BufferUsage::blk_read_time, BufferUsage::blk_write_time, ExplainState::buffers, Instrumentation::bufusage, CMD_DELETE, CMD_INSERT, CMD_UPDATE, ExplainState::costs, EXPLAIN_FORMAT_TEXT, explain_get_index_name(), ExplainCloseGroup(), ExplainIndexScanDetails(), ExplainMemberNodes(), ExplainModifyTarget(), ExplainOpenGroup(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyLong(), ExplainPropertyText(), ExplainScanTarget(), ExplainSubPlans(), ExplainState::format, ExplainState::indent, BitmapIndexScan::indexid, IndexOnlyScan::indexid, IndexScan::indexid, IndexOnlyScan::indexorderdir, IndexScan::indexorderdir, PlanState::initPlan, innerPlanState, INSTR_TIME_GET_MILLISEC, INSTR_TIME_IS_ZERO, InstrEndLoop(), PlanState::instrument, IsA, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, lcons(), list_delete_first(), list_length(), list_make1, BufferUsage::local_blks_dirtied, BufferUsage::local_blks_hit, BufferUsage::local_blks_read, BufferUsage::local_blks_written, make_orclause(), Instrumentation::need_timer, Instrumentation::nloops, nodeTag, Instrumentation::ntuples, NULL, outerPlanState, PlanState::plan, Plan::plan_rows, Plan::plan_width, SETOP_HASHED, SETOP_SORTED, SETOPCMD_EXCEPT, SETOPCMD_EXCEPT_ALL, SETOPCMD_INTERSECT, SETOPCMD_INTERSECT_ALL, BufferUsage::shared_blks_dirtied, BufferUsage::shared_blks_hit, BufferUsage::shared_blks_read, BufferUsage::shared_blks_written, show_expression(), show_foreignscan_info(), show_hash_info(), show_instrumentation_count(), show_merge_append_keys(), show_modifytable_info(), show_plan_tlist(), show_scan_qual(), show_sort_info(), show_sort_keys(), show_upper_qual(), Instrumentation::startup, Plan::startup_cost, ExplainState::str, PlanState::subPlan, T_Agg, T_Append, T_BitmapAnd, T_BitmapHeapScan, T_BitmapIndexScan, T_BitmapOr, T_CteScan, T_ForeignScan, T_FunctionScan, T_Group, T_Hash, T_HashJoin, T_IndexOnlyScan, T_IndexScan, T_Limit, T_LockRows, T_Material, T_MergeAppend, T_MergeJoin, T_ModifyTable, T_NestLoop, T_RecursiveUnion, T_Result, T_SeqScan, T_SetOp, T_Sort, T_SubqueryScan, T_TidScan, T_Unique, T_ValuesScan, T_WindowAgg, T_WorkTableScan, BufferUsage::temp_blks_read, BufferUsage::temp_blks_written, Instrumentation::total, Plan::total_cost, usage(), and ExplainState::verbose.
Referenced by ExplainMemberNodes(), ExplainPrintPlan(), and ExplainSubPlans().
{
Plan *plan = planstate->plan;
const char *pname; /* node type name for text output */
const char *sname; /* node type name for non-text output */
const char *strategy = NULL;
const char *operation = NULL;
int save_indent = es->indent;
bool haschildren;
switch (nodeTag(plan))
{
case T_Result:
pname = sname = "Result";
break;
case T_ModifyTable:
sname = "ModifyTable";
switch (((ModifyTable *) plan)->operation)
{
case CMD_INSERT:
pname = operation = "Insert";
break;
case CMD_UPDATE:
pname = operation = "Update";
break;
case CMD_DELETE:
pname = operation = "Delete";
break;
default:
pname = "???";
break;
}
break;
case T_Append:
pname = sname = "Append";
break;
case T_MergeAppend:
pname = sname = "Merge Append";
break;
case T_RecursiveUnion:
pname = sname = "Recursive Union";
break;
case T_BitmapAnd:
pname = sname = "BitmapAnd";
break;
case T_BitmapOr:
pname = sname = "BitmapOr";
break;
case T_NestLoop:
pname = sname = "Nested Loop";
break;
case T_MergeJoin:
pname = "Merge"; /* "Join" gets added by jointype switch */
sname = "Merge Join";
break;
case T_HashJoin:
pname = "Hash"; /* "Join" gets added by jointype switch */
sname = "Hash Join";
break;
case T_SeqScan:
pname = sname = "Seq Scan";
break;
case T_IndexScan:
pname = sname = "Index Scan";
break;
case T_IndexOnlyScan:
pname = sname = "Index Only Scan";
break;
case T_BitmapIndexScan:
pname = sname = "Bitmap Index Scan";
break;
case T_BitmapHeapScan:
pname = sname = "Bitmap Heap Scan";
break;
case T_TidScan:
pname = sname = "Tid Scan";
break;
case T_SubqueryScan:
pname = sname = "Subquery Scan";
break;
case T_FunctionScan:
pname = sname = "Function Scan";
break;
case T_ValuesScan:
pname = sname = "Values Scan";
break;
case T_CteScan:
pname = sname = "CTE Scan";
break;
case T_WorkTableScan:
pname = sname = "WorkTable Scan";
break;
case T_ForeignScan:
pname = sname = "Foreign Scan";
break;
case T_Material:
pname = sname = "Materialize";
break;
case T_Sort:
pname = sname = "Sort";
break;
case T_Group:
pname = sname = "Group";
break;
case T_Agg:
sname = "Aggregate";
switch (((Agg *) plan)->aggstrategy)
{
case AGG_PLAIN:
pname = "Aggregate";
strategy = "Plain";
break;
case AGG_SORTED:
pname = "GroupAggregate";
strategy = "Sorted";
break;
case AGG_HASHED:
pname = "HashAggregate";
strategy = "Hashed";
break;
default:
pname = "Aggregate ???";
strategy = "???";
break;
}
break;
case T_WindowAgg:
pname = sname = "WindowAgg";
break;
case T_Unique:
pname = sname = "Unique";
break;
case T_SetOp:
sname = "SetOp";
switch (((SetOp *) plan)->strategy)
{
case SETOP_SORTED:
pname = "SetOp";
strategy = "Sorted";
break;
case SETOP_HASHED:
pname = "HashSetOp";
strategy = "Hashed";
break;
default:
pname = "SetOp ???";
strategy = "???";
break;
}
break;
case T_LockRows:
pname = sname = "LockRows";
break;
case T_Limit:
pname = sname = "Limit";
break;
case T_Hash:
pname = sname = "Hash";
break;
default:
pname = sname = "???";
break;
}
ExplainOpenGroup("Plan",
relationship ? NULL : "Plan",
true, es);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
if (plan_name)
{
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfo(es->str, "%s\n", plan_name);
es->indent++;
}
if (es->indent)
{
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfoString(es->str, "-> ");
es->indent += 2;
}
appendStringInfoString(es->str, pname);
es->indent++;
}
else
{
ExplainPropertyText("Node Type", sname, es);
if (strategy)
ExplainPropertyText("Strategy", strategy, es);
if (operation)
ExplainPropertyText("Operation", operation, es);
if (relationship)
ExplainPropertyText("Parent Relationship", relationship, es);
if (plan_name)
ExplainPropertyText("Subplan Name", plan_name, es);
}
switch (nodeTag(plan))
{
case T_SeqScan:
case T_BitmapHeapScan:
case T_TidScan:
case T_SubqueryScan:
case T_FunctionScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_ForeignScan:
ExplainScanTarget((Scan *) plan, es);
break;
case T_IndexScan:
{
IndexScan *indexscan = (IndexScan *) plan;
ExplainIndexScanDetails(indexscan->indexid,
indexscan->indexorderdir,
es);
ExplainScanTarget((Scan *) indexscan, es);
}
break;
case T_IndexOnlyScan:
{
IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
ExplainIndexScanDetails(indexonlyscan->indexid,
indexonlyscan->indexorderdir,
es);
ExplainScanTarget((Scan *) indexonlyscan, es);
}
break;
case T_BitmapIndexScan:
{
BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
const char *indexname =
explain_get_index_name(bitmapindexscan->indexid);
if (es->format == EXPLAIN_FORMAT_TEXT)
appendStringInfo(es->str, " on %s", indexname);
else
ExplainPropertyText("Index Name", indexname, es);
}
break;
case T_ModifyTable:
ExplainModifyTarget((ModifyTable *) plan, es);
break;
case T_NestLoop:
case T_MergeJoin:
case T_HashJoin:
{
const char *jointype;
switch (((Join *) plan)->jointype)
{
case JOIN_INNER:
jointype = "Inner";
break;
case JOIN_LEFT:
jointype = "Left";
break;
case JOIN_FULL:
jointype = "Full";
break;
case JOIN_RIGHT:
jointype = "Right";
break;
case JOIN_SEMI:
jointype = "Semi";
break;
case JOIN_ANTI:
jointype = "Anti";
break;
default:
jointype = "???";
break;
}
if (es->format == EXPLAIN_FORMAT_TEXT)
{
/*
* For historical reasons, the join type is interpolated
* into the node type name...
*/
if (((Join *) plan)->jointype != JOIN_INNER)
appendStringInfo(es->str, " %s Join", jointype);
else if (!IsA(plan, NestLoop))
appendStringInfo(es->str, " Join");
}
else
ExplainPropertyText("Join Type", jointype, es);
}
break;
case T_SetOp:
{
const char *setopcmd;
switch (((SetOp *) plan)->cmd)
{
case SETOPCMD_INTERSECT:
setopcmd = "Intersect";
break;
case SETOPCMD_INTERSECT_ALL:
setopcmd = "Intersect All";
break;
case SETOPCMD_EXCEPT:
setopcmd = "Except";
break;
case SETOPCMD_EXCEPT_ALL:
setopcmd = "Except All";
break;
default:
setopcmd = "???";
break;
}
if (es->format == EXPLAIN_FORMAT_TEXT)
appendStringInfo(es->str, " %s", setopcmd);
else
ExplainPropertyText("Command", setopcmd, es);
}
break;
default:
break;
}
if (es->costs)
{
if (es->format == EXPLAIN_FORMAT_TEXT)
{
appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
plan->startup_cost, plan->total_cost,
plan->plan_rows, plan->plan_width);
}
else
{
ExplainPropertyFloat("Startup Cost", plan->startup_cost, 2, es);
ExplainPropertyFloat("Total Cost", plan->total_cost, 2, es);
ExplainPropertyFloat("Plan Rows", plan->plan_rows, 0, es);
ExplainPropertyInteger("Plan Width", plan->plan_width, es);
}
}
/*
* We have to forcibly clean up the instrumentation state because we
* haven't done ExecutorEnd yet. This is pretty grotty ...
*/
if (planstate->instrument)
InstrEndLoop(planstate->instrument);
if (planstate->instrument && planstate->instrument->nloops > 0)
{
double nloops = planstate->instrument->nloops;
double startup_sec = 1000.0 * planstate->instrument->startup / nloops;
double total_sec = 1000.0 * planstate->instrument->total / nloops;
double rows = planstate->instrument->ntuples / nloops;
if (es->format == EXPLAIN_FORMAT_TEXT)
{
if (planstate->instrument->need_timer)
appendStringInfo(es->str,
" (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
startup_sec, total_sec, rows, nloops);
else
appendStringInfo(es->str,
" (actual rows=%.0f loops=%.0f)",
rows, nloops);
}
else
{
if (planstate->instrument->need_timer)
{
ExplainPropertyFloat("Actual Startup Time", startup_sec, 3, es);
ExplainPropertyFloat("Actual Total Time", total_sec, 3, es);
}
ExplainPropertyFloat("Actual Rows", rows, 0, es);
ExplainPropertyFloat("Actual Loops", nloops, 0, es);
}
}
else if (es->analyze)
{
if (es->format == EXPLAIN_FORMAT_TEXT)
appendStringInfo(es->str, " (never executed)");
else if (planstate->instrument->need_timer)
{
ExplainPropertyFloat("Actual Startup Time", 0.0, 3, es);
ExplainPropertyFloat("Actual Total Time", 0.0, 3, es);
}
else
{
ExplainPropertyFloat("Actual Rows", 0.0, 0, es);
ExplainPropertyFloat("Actual Loops", 0.0, 0, es);
}
}
/* in text format, first line ends here */
if (es->format == EXPLAIN_FORMAT_TEXT)
appendStringInfoChar(es->str, '\n');
/* target list */
if (es->verbose)
show_plan_tlist(planstate, ancestors, es);
/* quals, sort keys, etc */
switch (nodeTag(plan))
{
case T_IndexScan:
show_scan_qual(((IndexScan *) plan)->indexqualorig,
"Index Cond", planstate, ancestors, es);
if (((IndexScan *) plan)->indexqualorig)
show_instrumentation_count("Rows Removed by Index Recheck", 2,
planstate, es);
show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
"Order By", planstate, ancestors, es);
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_IndexOnlyScan:
show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
"Index Cond", planstate, ancestors, es);
if (((IndexOnlyScan *) plan)->indexqual)
show_instrumentation_count("Rows Removed by Index Recheck", 2,
planstate, es);
show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
"Order By", planstate, ancestors, es);
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
if (es->analyze)
ExplainPropertyLong("Heap Fetches",
((IndexOnlyScanState *) planstate)->ioss_HeapFetches, es);
break;
case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
"Index Cond", planstate, ancestors, es);
break;
case T_BitmapHeapScan:
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
"Recheck Cond", planstate, ancestors, es);
if (((BitmapHeapScan *) plan)->bitmapqualorig)
show_instrumentation_count("Rows Removed by Index Recheck", 2,
planstate, es);
/* FALL THRU */
case T_SeqScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_SubqueryScan:
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_FunctionScan:
if (es->verbose)
show_expression(((FunctionScan *) plan)->funcexpr,
"Function Call", planstate, ancestors,
es->verbose, es);
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_TidScan:
{
/*
* The tidquals list has OR semantics, so be sure to show it
* as an OR condition.
*/
List *tidquals = ((TidScan *) plan)->tidquals;
if (list_length(tidquals) > 1)
tidquals = list_make1(make_orclause(tidquals));
show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
}
break;
case T_ForeignScan:
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
show_foreignscan_info((ForeignScanState *) planstate, es);
break;
case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual,
"Join Filter", planstate, ancestors, es);
if (((NestLoop *) plan)->join.joinqual)
show_instrumentation_count("Rows Removed by Join Filter", 1,
planstate, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 2,
planstate, es);
break;
case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses,
"Merge Cond", planstate, ancestors, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual,
"Join Filter", planstate, ancestors, es);
if (((MergeJoin *) plan)->join.joinqual)
show_instrumentation_count("Rows Removed by Join Filter", 1,
planstate, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 2,
planstate, es);
break;
case T_HashJoin:
show_upper_qual(((HashJoin *) plan)->hashclauses,
"Hash Cond", planstate, ancestors, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual,
"Join Filter", planstate, ancestors, es);
if (((HashJoin *) plan)->join.joinqual)
show_instrumentation_count("Rows Removed by Join Filter", 1,
planstate, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 2,
planstate, es);
break;
case T_Agg:
case T_Group:
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_Sort:
show_sort_keys((SortState *) planstate, ancestors, es);
show_sort_info((SortState *) planstate, es);
break;
case T_MergeAppend:
show_merge_append_keys((MergeAppendState *) planstate,
ancestors, es);
break;
case T_Result:
show_upper_qual((List *) ((Result *) plan)->resconstantqual,
"One-Time Filter", planstate, ancestors, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_ModifyTable:
show_modifytable_info((ModifyTableState *) planstate, es);
break;
case T_Hash:
show_hash_info((HashState *) planstate, es);
break;
default:
break;
}
/* Show buffer usage */
if (es->buffers)
{
const BufferUsage *usage = &planstate->instrument->bufusage;
if (es->format == EXPLAIN_FORMAT_TEXT)
{
bool has_shared = (usage->shared_blks_hit > 0 ||
usage->shared_blks_read > 0 ||
usage->shared_blks_dirtied > 0 ||
usage->shared_blks_written > 0);
bool has_local = (usage->local_blks_hit > 0 ||
usage->local_blks_read > 0 ||
usage->local_blks_dirtied > 0 ||
usage->local_blks_written > 0);
bool has_temp = (usage->temp_blks_read > 0 ||
usage->temp_blks_written > 0);
bool has_timing = (!INSTR_TIME_IS_ZERO(usage->blk_read_time) ||
!INSTR_TIME_IS_ZERO(usage->blk_write_time));
/* Show only positive counter values. */
if (has_shared || has_local || has_temp)
{
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfoString(es->str, "Buffers:");
if (has_shared)
{
appendStringInfoString(es->str, " shared");
if (usage->shared_blks_hit > 0)
appendStringInfo(es->str, " hit=%ld",
usage->shared_blks_hit);
if (usage->shared_blks_read > 0)
appendStringInfo(es->str, " read=%ld",
usage->shared_blks_read);
if (usage->shared_blks_dirtied > 0)
appendStringInfo(es->str, " dirtied=%ld",
usage->shared_blks_dirtied);
if (usage->shared_blks_written > 0)
appendStringInfo(es->str, " written=%ld",
usage->shared_blks_written);
if (has_local || has_temp)
appendStringInfoChar(es->str, ',');
}
if (has_local)
{
appendStringInfoString(es->str, " local");
if (usage->local_blks_hit > 0)
appendStringInfo(es->str, " hit=%ld",
usage->local_blks_hit);
if (usage->local_blks_read > 0)
appendStringInfo(es->str, " read=%ld",
usage->local_blks_read);
if (usage->local_blks_dirtied > 0)
appendStringInfo(es->str, " dirtied=%ld",
usage->local_blks_dirtied);
if (usage->local_blks_written > 0)
appendStringInfo(es->str, " written=%ld",
usage->local_blks_written);
if (has_temp)
appendStringInfoChar(es->str, ',');
}
if (has_temp)
{
appendStringInfoString(es->str, " temp");
if (usage->temp_blks_read > 0)
appendStringInfo(es->str, " read=%ld",
usage->temp_blks_read);
if (usage->temp_blks_written > 0)
appendStringInfo(es->str, " written=%ld",
usage->temp_blks_written);
}
appendStringInfoChar(es->str, '\n');
}
/* As above, show only positive counter values. */
if (has_timing)
{
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfoString(es->str, "I/O Timings:");
if (!INSTR_TIME_IS_ZERO(usage->blk_read_time))
appendStringInfo(es->str, " read=%0.3f",
INSTR_TIME_GET_MILLISEC(usage->blk_read_time));
if (!INSTR_TIME_IS_ZERO(usage->blk_write_time))
appendStringInfo(es->str, " write=%0.3f",
INSTR_TIME_GET_MILLISEC(usage->blk_write_time));
appendStringInfoChar(es->str, '\n');
}
}
else
{
ExplainPropertyLong("Shared Hit Blocks", usage->shared_blks_hit, es);
ExplainPropertyLong("Shared Read Blocks", usage->shared_blks_read, es);
ExplainPropertyLong("Shared Dirtied Blocks", usage->shared_blks_dirtied, es);
ExplainPropertyLong("Shared Written Blocks", usage->shared_blks_written, es);
ExplainPropertyLong("Local Hit Blocks", usage->local_blks_hit, es);
ExplainPropertyLong("Local Read Blocks", usage->local_blks_read, es);
ExplainPropertyLong("Local Dirtied Blocks", usage->local_blks_dirtied, es);
ExplainPropertyLong("Local Written Blocks", usage->local_blks_written, es);
ExplainPropertyLong("Temp Read Blocks", usage->temp_blks_read, es);
ExplainPropertyLong("Temp Written Blocks", usage->temp_blks_written, es);
ExplainPropertyFloat("I/O Read Time", INSTR_TIME_GET_MILLISEC(usage->blk_read_time), 3, es);
ExplainPropertyFloat("I/O Write Time", INSTR_TIME_GET_MILLISEC(usage->blk_write_time), 3, es);
}
}
/* Get ready to display the child plans */
haschildren = planstate->initPlan ||
outerPlanState(planstate) ||
innerPlanState(planstate) ||
IsA(plan, ModifyTable) ||
IsA(plan, Append) ||
IsA(plan, MergeAppend) ||
IsA(plan, BitmapAnd) ||
IsA(plan, BitmapOr) ||
IsA(plan, SubqueryScan) ||
planstate->subPlan;
if (haschildren)
{
ExplainOpenGroup("Plans", "Plans", false, es);
/* Pass current PlanState as head of ancestors list for children */
ancestors = lcons(planstate, ancestors);
}
/* initPlan-s */
if (planstate->initPlan)
ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
/* lefttree */
if (outerPlanState(planstate))
ExplainNode(outerPlanState(planstate), ancestors,
"Outer", NULL, es);
/* righttree */
if (innerPlanState(planstate))
ExplainNode(innerPlanState(planstate), ancestors,
"Inner", NULL, es);
/* special child plans */
switch (nodeTag(plan))
{
case T_ModifyTable:
ExplainMemberNodes(((ModifyTable *) plan)->plans,
((ModifyTableState *) planstate)->mt_plans,
ancestors, es);
break;
case T_Append:
ExplainMemberNodes(((Append *) plan)->appendplans,
((AppendState *) planstate)->appendplans,
ancestors, es);
break;
case T_MergeAppend:
ExplainMemberNodes(((MergeAppend *) plan)->mergeplans,
((MergeAppendState *) planstate)->mergeplans,
ancestors, es);
break;
case T_BitmapAnd:
ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
((BitmapAndState *) planstate)->bitmapplans,
ancestors, es);
break;
case T_BitmapOr:
ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
((BitmapOrState *) planstate)->bitmapplans,
ancestors, es);
break;
case T_SubqueryScan:
ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
"Subquery", NULL, es);
break;
default:
break;
}
/* subPlan-s */
if (planstate->subPlan)
ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
/* end of child plans */
if (haschildren)
{
ancestors = list_delete_first(ancestors);
ExplainCloseGroup("Plans", "Plans", false, es);
}
/* in text format, undo whatever indentation we added */
if (es->format == EXPLAIN_FORMAT_TEXT)
es->indent = save_indent;
ExplainCloseGroup("Plan",
relationship ? NULL : "Plan",
true, es);
}
| 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);
}
| static void ExplainOneQuery | ( | Query * | query, | |
| IntoClause * | into, | |||
| ExplainState * | es, | |||
| const char * | queryString, | |||
| ParamListInfo | params | |||
| ) | [static] |
Definition at line 302 of file explain.c.
References CMD_UTILITY, Query::commandType, ExplainOnePlan(), ExplainOneQuery_hook, ExplainOneUtility(), pg_plan_query(), and Query::utilityStmt.
Referenced by ExplainOneUtility(), and ExplainQuery().
{
/* planner will not cope with utility statements */
if (query->commandType == CMD_UTILITY)
{
ExplainOneUtility(query->utilityStmt, into, es, queryString, params);
return;
}
/* if an advisor plugin is present, let it manage things */
if (ExplainOneQuery_hook)
(*ExplainOneQuery_hook) (query, into, es, queryString, params);
else
{
PlannedStmt *plan;
/* plan the query */
plan = pg_plan_query(query, 0, params);
/* run it (if needed) and produce output */
ExplainOnePlan(plan, into, es, queryString, params);
}
}
| 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);
}
}
| static void ExplainOpenGroup | ( | const char * | objtype, | |
| const char * | labelname, | |||
| bool | labeled, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 2304 of file explain.c.
References appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), escape_json(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainJSONLineEnding(), ExplainXMLTag(), ExplainYAMLLineStarting(), ExplainState::format, ExplainState::grouping_stack, ExplainState::indent, lcons_int(), ExplainState::str, and X_OPENING.
Referenced by ExplainNode(), ExplainOnePlan(), and report_triggers().
{
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
/* nothing to do */
break;
case EXPLAIN_FORMAT_XML:
ExplainXMLTag(objtype, X_OPENING, es);
es->indent++;
break;
case EXPLAIN_FORMAT_JSON:
ExplainJSONLineEnding(es);
appendStringInfoSpaces(es->str, 2 * es->indent);
if (labelname)
{
escape_json(es->str, labelname);
appendStringInfoString(es->str, ": ");
}
appendStringInfoChar(es->str, labeled ? '{' : '[');
/*
* In JSON format, the grouping_stack is an integer list. 0 means
* we've emitted nothing at this grouping level, 1 means we've
* emitted something (and so the next item needs a comma). See
* ExplainJSONLineEnding().
*/
es->grouping_stack = lcons_int(0, es->grouping_stack);
es->indent++;
break;
case EXPLAIN_FORMAT_YAML:
/*
* In YAML format, the grouping stack is an integer list. 0 means
* we've emitted nothing at this grouping level AND this grouping
* level is unlabelled and must be marked with "- ". See
* ExplainYAMLLineStarting().
*/
ExplainYAMLLineStarting(es);
if (labelname)
{
appendStringInfo(es->str, "%s: ", labelname);
es->grouping_stack = lcons_int(1, es->grouping_stack);
}
else
{
appendStringInfoString(es->str, "- ");
es->grouping_stack = lcons_int(0, es->grouping_stack);
}
es->indent++;
break;
}
}
| static void ExplainPreScanMemberNodes | ( | List * | plans, | |
| PlanState ** | planstates, | |||
| Bitmapset ** | rels_used | |||
| ) | [static] |
Definition at line 752 of file explain.c.
References ExplainPreScanNode(), and list_length().
Referenced by ExplainPreScanNode().
{
int nplans = list_length(plans);
int j;
for (j = 0; j < nplans; j++)
ExplainPreScanNode(planstates[j], rels_used);
}
Definition at line 662 of file explain.c.
References bms_add_member(), ExplainPreScanMemberNodes(), ExplainPreScanSubPlans(), PlanState::initPlan, innerPlanState, linitial_int, nodeTag, outerPlanState, PlanState::plan, PlanState::subPlan, T_Append, T_BitmapAnd, T_BitmapHeapScan, T_BitmapOr, T_CteScan, T_ForeignScan, T_FunctionScan, T_IndexOnlyScan, T_IndexScan, T_MergeAppend, T_ModifyTable, T_SeqScan, T_SubqueryScan, T_TidScan, T_ValuesScan, and T_WorkTableScan.
Referenced by ExplainPreScanMemberNodes(), ExplainPreScanSubPlans(), and ExplainPrintPlan().
{
Plan *plan = planstate->plan;
switch (nodeTag(plan))
{
case T_SeqScan:
case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan:
case T_TidScan:
case T_SubqueryScan:
case T_FunctionScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_ForeignScan:
*rels_used = bms_add_member(*rels_used,
((Scan *) plan)->scanrelid);
break;
case T_ModifyTable:
/* cf ExplainModifyTarget */
*rels_used = bms_add_member(*rels_used,
linitial_int(((ModifyTable *) plan)->resultRelations));
break;
default:
break;
}
/* initPlan-s */
if (planstate->initPlan)
ExplainPreScanSubPlans(planstate->initPlan, rels_used);
/* lefttree */
if (outerPlanState(planstate))
ExplainPreScanNode(outerPlanState(planstate), rels_used);
/* righttree */
if (innerPlanState(planstate))
ExplainPreScanNode(innerPlanState(planstate), rels_used);
/* special child plans */
switch (nodeTag(plan))
{
case T_ModifyTable:
ExplainPreScanMemberNodes(((ModifyTable *) plan)->plans,
((ModifyTableState *) planstate)->mt_plans,
rels_used);
break;
case T_Append:
ExplainPreScanMemberNodes(((Append *) plan)->appendplans,
((AppendState *) planstate)->appendplans,
rels_used);
break;
case T_MergeAppend:
ExplainPreScanMemberNodes(((MergeAppend *) plan)->mergeplans,
((MergeAppendState *) planstate)->mergeplans,
rels_used);
break;
case T_BitmapAnd:
ExplainPreScanMemberNodes(((BitmapAnd *) plan)->bitmapplans,
((BitmapAndState *) planstate)->bitmapplans,
rels_used);
break;
case T_BitmapOr:
ExplainPreScanMemberNodes(((BitmapOr *) plan)->bitmapplans,
((BitmapOrState *) planstate)->bitmapplans,
rels_used);
break;
case T_SubqueryScan:
ExplainPreScanNode(((SubqueryScanState *) planstate)->subplan,
rels_used);
break;
default:
break;
}
/* subPlan-s */
if (planstate->subPlan)
ExplainPreScanSubPlans(planstate->subPlan, rels_used);
}
Definition at line 766 of file explain.c.
References ExplainPreScanNode(), lfirst, and SubPlanState::planstate.
Referenced by ExplainPreScanNode().
{
ListCell *lst;
foreach(lst, plans)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
ExplainPreScanNode(sps->planstate, rels_used);
}
}
| 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);
}
| static void ExplainProperty | ( | const char * | qlabel, | |
| const char * | value, | |||
| bool | numeric, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 2201 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, pfree(), ExplainState::str, X_CLOSING, X_NOWHITESPACE, and X_OPENING.
Referenced by ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyLong(), and ExplainPropertyText().
{
switch (es->format)
{
case EXPLAIN_FORMAT_TEXT:
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfo(es->str, "%s: %s\n", qlabel, value);
break;
case EXPLAIN_FORMAT_XML:
{
char *str;
appendStringInfoSpaces(es->str, es->indent * 2);
ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
str = escape_xml(value);
appendStringInfoString(es->str, str);
pfree(str);
ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
appendStringInfoChar(es->str, '\n');
}
break;
case EXPLAIN_FORMAT_JSON:
ExplainJSONLineEnding(es);
appendStringInfoSpaces(es->str, es->indent * 2);
escape_json(es->str, qlabel);
appendStringInfoString(es->str, ": ");
if (numeric)
appendStringInfoString(es->str, value);
else
escape_json(es->str, value);
break;
case EXPLAIN_FORMAT_YAML:
ExplainYAMLLineStarting(es);
appendStringInfo(es->str, "%s: ", qlabel);
if (numeric)
appendStringInfoString(es->str, value);
else
escape_yaml(es->str, value);
break;
}
}
| 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;
}
| static void ExplainScanTarget | ( | Scan * | plan, | |
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1925 of file explain.c.
References ExplainTargetRel().
Referenced by ExplainNode().
{
ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
}
| 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;
}
}
| static void ExplainSubPlans | ( | List * | plans, | |
| List * | ancestors, | |||
| const char * | relationship, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 2106 of file explain.c.
References ExplainNode(), ExprState::expr, lfirst, SubPlan::plan_name, SubPlanState::planstate, and SubPlanState::xprstate.
Referenced by ExplainNode().
{
ListCell *lst;
foreach(lst, plans)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
ExplainNode(sps->planstate, ancestors,
relationship, sp->plan_name, es);
}
}
| static void ExplainTargetRel | ( | Plan * | plan, | |
| Index | rti, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1952 of file explain.c.
References Alias::aliasname, appendStringInfo(), appendStringInfoString(), Assert, RangeTblEntry::eref, EXPLAIN_FORMAT_TEXT, ExplainPropertyText(), ExplainState::format, get_func_name(), get_namespace_name(), get_rel_name(), IsA, list_nth(), nodeTag, NULL, quote_identifier(), RangeTblEntry::relid, rt_fetch, ExplainState::rtable, ExplainState::rtable_names, RTE_CTE, RTE_FUNCTION, RTE_RELATION, RTE_VALUES, RangeTblEntry::rtekind, ExplainState::str, T_BitmapHeapScan, T_CteScan, T_ForeignScan, T_FunctionScan, T_IndexOnlyScan, T_IndexScan, T_ModifyTable, T_SeqScan, T_TidScan, T_ValuesScan, T_WorkTableScan, and ExplainState::verbose.
Referenced by ExplainModifyTarget(), and ExplainScanTarget().
{
char *objectname = NULL;
char *namespace = NULL;
const char *objecttag = NULL;
RangeTblEntry *rte;
char *refname;
rte = rt_fetch(rti, es->rtable);
refname = (char *) list_nth(es->rtable_names, rti - 1);
if (refname == NULL)
refname = rte->eref->aliasname;
switch (nodeTag(plan))
{
case T_SeqScan:
case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan:
case T_TidScan:
case T_ForeignScan:
case T_ModifyTable:
/* Assert it's on a real relation */
Assert(rte->rtekind == RTE_RELATION);
objectname = get_rel_name(rte->relid);
if (es->verbose)
namespace = get_namespace_name(get_rel_namespace(rte->relid));
objecttag = "Relation Name";
break;
case T_FunctionScan:
{
Node *funcexpr;
/* Assert it's on a RangeFunction */
Assert(rte->rtekind == RTE_FUNCTION);
/*
* If the expression is still a function call, we can get the
* real name of the function. Otherwise, punt (this can
* happen if the optimizer simplified away the function call,
* for example).
*/
funcexpr = ((FunctionScan *) plan)->funcexpr;
if (funcexpr && IsA(funcexpr, FuncExpr))
{
Oid funcid = ((FuncExpr *) funcexpr)->funcid;
objectname = get_func_name(funcid);
if (es->verbose)
namespace =
get_namespace_name(get_func_namespace(funcid));
}
objecttag = "Function Name";
}
break;
case T_ValuesScan:
Assert(rte->rtekind == RTE_VALUES);
break;
case T_CteScan:
/* Assert it's on a non-self-reference CTE */
Assert(rte->rtekind == RTE_CTE);
Assert(!rte->self_reference);
objectname = rte->ctename;
objecttag = "CTE Name";
break;
case T_WorkTableScan:
/* Assert it's on a self-reference CTE */
Assert(rte->rtekind == RTE_CTE);
Assert(rte->self_reference);
objectname = rte->ctename;
objecttag = "CTE Name";
break;
default:
break;
}
if (es->format == EXPLAIN_FORMAT_TEXT)
{
appendStringInfoString(es->str, " on");
if (namespace != NULL)
appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
quote_identifier(objectname));
else if (objectname != NULL)
appendStringInfo(es->str, " %s", quote_identifier(objectname));
if (objectname == NULL || strcmp(refname, objectname) != 0)
appendStringInfo(es->str, " %s", quote_identifier(refname));
}
else
{
if (objecttag != NULL && objectname != NULL)
ExplainPropertyText(objecttag, objectname, es);
if (namespace != NULL)
ExplainPropertyText("Schema", namespace, es);
ExplainPropertyText("Alias", refname, es);
}
}
| static void ExplainXMLTag | ( | const char * | tagname, | |
| int | flags, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 2537 of file explain.c.
References appendStringInfoCharMacro, appendStringInfoSpaces(), appendStringInfoString(), ExplainState::indent, ExplainState::str, X_CLOSE_IMMEDIATE, X_CLOSING, and X_NOWHITESPACE.
Referenced by ExplainCloseGroup(), ExplainDummyGroup(), ExplainOpenGroup(), ExplainProperty(), and ExplainPropertyList().
{
const char *s;
if ((flags & X_NOWHITESPACE) == 0)
appendStringInfoSpaces(es->str, 2 * es->indent);
appendStringInfoCharMacro(es->str, '<');
if ((flags & X_CLOSING) != 0)
appendStringInfoCharMacro(es->str, '/');
for (s = tagname; *s; s++)
appendStringInfoCharMacro(es->str, (*s == ' ') ? '-' : *s);
if ((flags & X_CLOSE_IMMEDIATE) != 0)
appendStringInfoString(es->str, " /");
appendStringInfoCharMacro(es->str, '>');
if ((flags & X_NOWHITESPACE) == 0)
appendStringInfoCharMacro(es->str, '\n');
}
| static void ExplainYAMLLineStarting | ( | ExplainState * | es | ) | [static] |
Definition at line 2583 of file explain.c.
References appendStringInfoChar(), appendStringInfoSpaces(), Assert, EXPLAIN_FORMAT_YAML, ExplainState::format, ExplainState::grouping_stack, ExplainState::indent, linitial_int, and ExplainState::str.
Referenced by ExplainDummyGroup(), ExplainOpenGroup(), ExplainProperty(), and ExplainPropertyList().
{
Assert(es->format == EXPLAIN_FORMAT_YAML);
if (linitial_int(es->grouping_stack) == 0)
{
linitial_int(es->grouping_stack) = 1;
}
else
{
appendStringInfoChar(es->str, '\n');
appendStringInfoSpaces(es->str, es->indent * 2);
}
}
| static void report_triggers | ( | ResultRelInfo * | rInfo, | |
| bool | show_relname, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 577 of file explain.c.
References appendStringInfo(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyFloat(), ExplainPropertyText(), ExplainState::format, get_constraint_name(), InstrEndLoop(), Instrumentation::ntuples, NULL, TriggerDesc::numtriggers, OidIsValid, pfree(), RelationGetRelationName, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigInstrument, ExplainState::str, Trigger::tgconstraint, Trigger::tgname, Instrumentation::total, TriggerDesc::triggers, and ExplainState::verbose.
Referenced by ExplainOnePlan().
{
int nt;
if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
return;
for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
{
Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
char *relname;
char *conname = NULL;
/* Must clean up instrumentation state */
InstrEndLoop(instr);
/*
* We ignore triggers that were never invoked; they likely aren't
* relevant to the current query type.
*/
if (instr->ntuples == 0)
continue;
ExplainOpenGroup("Trigger", NULL, true, es);
relname = RelationGetRelationName(rInfo->ri_RelationDesc);
if (OidIsValid(trig->tgconstraint))
conname = get_constraint_name(trig->tgconstraint);
/*
* In text format, we avoid printing both the trigger name and the
* constraint name unless VERBOSE is specified. In non-text formats
* we just print everything.
*/
if (es->format == EXPLAIN_FORMAT_TEXT)
{
if (es->verbose || conname == NULL)
appendStringInfo(es->str, "Trigger %s", trig->tgname);
else
appendStringInfoString(es->str, "Trigger");
if (conname)
appendStringInfo(es->str, " for constraint %s", conname);
if (show_relname)
appendStringInfo(es->str, " on %s", relname);
appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
1000.0 * instr->total, instr->ntuples);
}
else
{
ExplainPropertyText("Trigger Name", trig->tgname, es);
if (conname)
ExplainPropertyText("Constraint Name", conname, es);
ExplainPropertyText("Relation", relname, es);
ExplainPropertyFloat("Time", 1000.0 * instr->total, 3, es);
ExplainPropertyFloat("Calls", instr->ntuples, 0, es);
}
if (conname)
pfree(conname);
ExplainCloseGroup("Trigger", NULL, true, es);
}
}
| static void show_expression | ( | Node * | node, | |
| const char * | qlabel, | |||
| PlanState * | planstate, | |||
| List * | ancestors, | |||
| bool | useprefix, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1603 of file explain.c.
References deparse_context_for_planstate(), deparse_expression(), ExplainPropertyText(), ExplainState::rtable, and ExplainState::rtable_names.
Referenced by ExplainNode(), and show_qual().
{
List *context;
char *exprstr;
/* Set up deparsing context */
context = deparse_context_for_planstate((Node *) planstate,
ancestors,
es->rtable,
es->rtable_names);
/* Deparse the expression */
exprstr = deparse_expression(node, context, useprefix, false);
/* And add to es->str */
ExplainPropertyText(qlabel, exprstr, es);
}
| static void show_foreignscan_info | ( | ForeignScanState * | fsstate, | |
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1847 of file explain.c.
References FdwRoutine::ExplainForeignScan, ForeignScanState::fdwroutine, and NULL.
Referenced by ExplainNode().
{
FdwRoutine *fdwroutine = fsstate->fdwroutine;
/* Let the FDW emit whatever fields it wants */
if (fdwroutine->ExplainForeignScan != NULL)
fdwroutine->ExplainForeignScan(fsstate, es);
}
| static void show_hash_info | ( | HashState * | hashstate, | |
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1774 of file explain.c.
References appendStringInfo(), appendStringInfoSpaces(), Assert, EXPLAIN_FORMAT_TEXT, ExplainPropertyLong(), ExplainState::format, HashState::hashtable, ExplainState::indent, IsA, HashJoinTableData::nbatch, HashJoinTableData::nbatch_original, HashJoinTableData::nbuckets, HashJoinTableData::spacePeak, and ExplainState::str.
Referenced by ExplainNode().
{
HashJoinTable hashtable;
Assert(IsA(hashstate, HashState));
hashtable = hashstate->hashtable;
if (hashtable)
{
long spacePeakKb = (hashtable->spacePeak + 1023) / 1024;
if (es->format != EXPLAIN_FORMAT_TEXT)
{
ExplainPropertyLong("Hash Buckets", hashtable->nbuckets, es);
ExplainPropertyLong("Hash Batches", hashtable->nbatch, es);
ExplainPropertyLong("Original Hash Batches",
hashtable->nbatch_original, es);
ExplainPropertyLong("Peak Memory Usage", spacePeakKb, es);
}
else if (hashtable->nbatch_original != hashtable->nbatch)
{
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfo(es->str,
"Buckets: %d Batches: %d (originally %d) Memory Usage: %ldkB\n",
hashtable->nbuckets, hashtable->nbatch,
hashtable->nbatch_original, spacePeakKb);
}
else
{
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfo(es->str,
"Buckets: %d Batches: %d Memory Usage: %ldkB\n",
hashtable->nbuckets, hashtable->nbatch,
spacePeakKb);
}
}
}
| static void show_instrumentation_count | ( | const char * | qlabel, | |
| int | which, | |||
| PlanState * | planstate, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1818 of file explain.c.
References ExplainState::analyze, EXPLAIN_FORMAT_TEXT, ExplainPropertyFloat(), ExplainState::format, PlanState::instrument, Instrumentation::nfiltered1, Instrumentation::nfiltered2, and Instrumentation::nloops.
Referenced by ExplainNode().
{
double nfiltered;
double nloops;
if (!es->analyze || !planstate->instrument)
return;
if (which == 2)
nfiltered = planstate->instrument->nfiltered2;
else
nfiltered = planstate->instrument->nfiltered1;
nloops = planstate->instrument->nloops;
/* In text mode, suppress zero counts; they're not interesting enough */
if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
{
if (nloops > 0)
ExplainPropertyFloat(qlabel, nfiltered / nloops, 0, es);
else
ExplainPropertyFloat(qlabel, 0.0, 0, es);
}
}
| static void show_merge_append_keys | ( | MergeAppendState * | mstate, | |
| List * | ancestors, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1689 of file explain.c.
References MergeAppend::numCols, PlanState::plan, MergeAppendState::ps, show_sort_keys_common(), and MergeAppend::sortColIdx.
Referenced by ExplainNode().
{
MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
show_sort_keys_common((PlanState *) mstate,
plan->numCols, plan->sortColIdx,
ancestors, es);
}
| static void show_modifytable_info | ( | ModifyTableState * | mtstate, | |
| ExplainState * | es | |||
| ) | [static] |
Definition at line 2053 of file explain.c.
References FdwRoutine::ExplainForeignModify, ModifyTable::fdwPrivLists, linitial, NULL, PlanState::plan, ModifyTableState::ps, ModifyTableState::resultRelInfo, and ResultRelInfo::ri_FdwRoutine.
Referenced by ExplainNode().
{
FdwRoutine *fdwroutine = mtstate->resultRelInfo->ri_FdwRoutine;
/*
* If the first target relation is a foreign table, call its FDW to
* display whatever additional fields it wants to. For now, we ignore the
* possibility of other targets being foreign tables, although the API for
* ExplainForeignModify is designed to allow them to be processed.
*/
if (fdwroutine != NULL &&
fdwroutine->ExplainForeignModify != NULL)
{
ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
List *fdw_private = (List *) linitial(node->fdwPrivLists);
fdwroutine->ExplainForeignModify(mtstate,
mtstate->resultRelInfo,
fdw_private,
0,
es);
}
}
| static void show_plan_tlist | ( | PlanState * | planstate, | |
| List * | ancestors, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1558 of file explain.c.
References deparse_context_for_planstate(), deparse_expression(), ExplainPropertyList(), TargetEntry::expr, IsA, lappend(), lfirst, list_length(), NIL, PlanState::plan, ExplainState::rtable, ExplainState::rtable_names, and Plan::targetlist.
Referenced by ExplainNode().
{
Plan *plan = planstate->plan;
List *context;
List *result = NIL;
bool useprefix;
ListCell *lc;
/* No work if empty tlist (this occurs eg in bitmap indexscans) */
if (plan->targetlist == NIL)
return;
/* The tlist of an Append isn't real helpful, so suppress it */
if (IsA(plan, Append))
return;
/* Likewise for MergeAppend and RecursiveUnion */
if (IsA(plan, MergeAppend))
return;
if (IsA(plan, RecursiveUnion))
return;
/* Set up deparsing context */
context = deparse_context_for_planstate((Node *) planstate,
ancestors,
es->rtable,
es->rtable_names);
useprefix = list_length(es->rtable) > 1;
/* Deparse each result column (we now include resjunk ones) */
foreach(lc, plan->targetlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
result = lappend(result,
deparse_expression((Node *) tle->expr, context,
useprefix, false));
}
/* Print results */
ExplainPropertyList("Output", result, es);
}
| static void show_qual | ( | List * | qual, | |
| const char * | qlabel, | |||
| PlanState * | planstate, | |||
| List * | ancestors, | |||
| bool | useprefix, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1627 of file explain.c.
References make_ands_explicit(), NIL, and show_expression().
Referenced by show_scan_qual(), and show_upper_qual().
{
Node *node;
/* No work if empty qual */
if (qual == NIL)
return;
/* Convert AND list to explicit AND */
node = (Node *) make_ands_explicit(qual);
/* And show it */
show_expression(node, qlabel, planstate, ancestors, useprefix, es);
}
| static void show_scan_qual | ( | List * | qual, | |
| const char * | qlabel, | |||
| PlanState * | planstate, | |||
| List * | ancestors, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1648 of file explain.c.
References IsA, PlanState::plan, show_qual(), and ExplainState::verbose.
Referenced by ExplainNode().
{
bool useprefix;
useprefix = (IsA(planstate->plan, SubqueryScan) ||es->verbose);
show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
}
| static void show_sort_info | ( | SortState * | sortstate, | |
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1742 of file explain.c.
References ExplainState::analyze, appendStringInfo(), appendStringInfoSpaces(), Assert, EXPLAIN_FORMAT_TEXT, ExplainPropertyLong(), ExplainPropertyText(), ExplainState::format, ExplainState::indent, IsA, NULL, SortState::sort_Done, ExplainState::str, tuplesort_get_stats(), and SortState::tuplesortstate.
Referenced by ExplainNode().
{
Assert(IsA(sortstate, SortState));
if (es->analyze && sortstate->sort_Done &&
sortstate->tuplesortstate != NULL)
{
Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
const char *sortMethod;
const char *spaceType;
long spaceUsed;
tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfo(es->str, "Sort Method: %s %s: %ldkB\n",
sortMethod, spaceType, spaceUsed);
}
else
{
ExplainPropertyText("Sort Method", sortMethod, es);
ExplainPropertyLong("Sort Space Used", spaceUsed, es);
ExplainPropertyText("Sort Space Type", spaceType, es);
}
}
}
| static void show_sort_keys | ( | SortState * | sortstate, | |
| List * | ancestors, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1676 of file explain.c.
References Sort::numCols, PlanState::plan, ScanState::ps, show_sort_keys_common(), Sort::sortColIdx, and SortState::ss.
Referenced by ExplainNode().
{
Sort *plan = (Sort *) sortstate->ss.ps.plan;
show_sort_keys_common((PlanState *) sortstate,
plan->numCols, plan->sortColIdx,
ancestors, es);
}
| static void show_sort_keys_common | ( | PlanState * | planstate, | |
| int | nkeys, | |||
| AttrNumber * | keycols, | |||
| List * | ancestors, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1700 of file explain.c.
References deparse_context_for_planstate(), deparse_expression(), elog, ERROR, ExplainPropertyList(), TargetEntry::expr, get_tle_by_resno(), lappend(), list_length(), PlanState::plan, ExplainState::rtable, ExplainState::rtable_names, Plan::targetlist, and ExplainState::verbose.
Referenced by show_merge_append_keys(), and show_sort_keys().
{
Plan *plan = planstate->plan;
List *context;
List *result = NIL;
bool useprefix;
int keyno;
char *exprstr;
if (nkeys <= 0)
return;
/* Set up deparsing context */
context = deparse_context_for_planstate((Node *) planstate,
ancestors,
es->rtable,
es->rtable_names);
useprefix = (list_length(es->rtable) > 1 || es->verbose);
for (keyno = 0; keyno < nkeys; keyno++)
{
/* find key expression in tlist */
AttrNumber keyresno = keycols[keyno];
TargetEntry *target = get_tle_by_resno(plan->targetlist,
keyresno);
if (!target)
elog(ERROR, "no tlist entry for key %d", keyresno);
/* Deparse the expression, showing any top-level cast */
exprstr = deparse_expression((Node *) target->expr, context,
useprefix, true);
result = lappend(result, exprstr);
}
ExplainPropertyList("Sort Key", result, es);
}
| static void show_upper_qual | ( | List * | qual, | |
| const char * | qlabel, | |||
| PlanState * | planstate, | |||
| List * | ancestors, | |||
| ExplainState * | es | |||
| ) | [static] |
Definition at line 1662 of file explain.c.
References list_length(), ExplainState::rtable, show_qual(), and ExplainState::verbose.
Referenced by ExplainNode().
{
bool useprefix;
useprefix = (list_length(es->rtable) > 1 || es->verbose);
show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
}
Definition at line 40 of file explain.c.
Referenced by explain_get_index_name().
Definition at line 37 of file explain.c.
Referenced by ExplainOneQuery().
1.7.1