PostgreSQL Source Code git master
explain.c File Reference
#include "postgres.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/createas.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/explain_dr.h"
#include "commands/explain_format.h"
#include "commands/explain_state.h"
#include "commands/prepare.h"
#include "foreign/fdwapi.h"
#include "jit/jit.h"
#include "libpq/pqformat.h"
#include "libpq/protocol.h"
#include "nodes/extensible.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/guc_tables.h"
#include "utils/json.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/tuplesort.h"
#include "utils/typcache.h"
#include "utils/xml.h"
Include dependency graph for explain.c:

Go to the source code of this file.

Macros

#define BYTES_TO_KILOBYTES(b)   (((b) + 1023) / 1024)
 

Functions

static void ExplainOneQuery (Query *query, int cursorOptions, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
 
static void ExplainPrintJIT (ExplainState *es, int jit_flags, JitInstrumentation *ji)
 
static void ExplainPrintSerialize (ExplainState *es, SerializeMetrics *metrics)
 
static void report_triggers (ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
 
static double elapsed_time (instr_time *starttime)
 
static bool ExplainPreScanNode (PlanState *planstate, 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_incremental_sort_keys (IncrementalSortState *incrsortstate, List *ancestors, ExplainState *es)
 
static void show_merge_append_keys (MergeAppendState *mstate, List *ancestors, ExplainState *es)
 
static void show_agg_keys (AggState *astate, List *ancestors, ExplainState *es)
 
static void show_grouping_sets (PlanState *planstate, Agg *agg, List *ancestors, ExplainState *es)
 
static void show_grouping_set_keys (PlanState *planstate, Agg *aggnode, Sort *sortnode, List *context, bool useprefix, List *ancestors, ExplainState *es)
 
static void show_group_keys (GroupState *gstate, List *ancestors, ExplainState *es)
 
static void show_sort_group_keys (PlanState *planstate, const char *qlabel, int nkeys, int nPresortedKeys, AttrNumber *keycols, Oid *sortOperators, Oid *collations, bool *nullsFirst, List *ancestors, ExplainState *es)
 
static void show_sortorder_options (StringInfo buf, Node *sortexpr, Oid sortOperator, Oid collation, bool nullsFirst)
 
static void show_window_def (WindowAggState *planstate, List *ancestors, ExplainState *es)
 
static void show_window_keys (StringInfo buf, PlanState *planstate, int nkeys, AttrNumber *keycols, List *ancestors, ExplainState *es)
 
static void show_storage_info (char *maxStorageType, int64 maxSpaceUsed, ExplainState *es)
 
static void show_tablesample (TableSampleClause *tsc, PlanState *planstate, List *ancestors, ExplainState *es)
 
static void show_sort_info (SortState *sortstate, ExplainState *es)
 
static void show_incremental_sort_info (IncrementalSortState *incrsortstate, ExplainState *es)
 
static void show_hash_info (HashState *hashstate, ExplainState *es)
 
static void show_material_info (MaterialState *mstate, ExplainState *es)
 
static void show_windowagg_info (WindowAggState *winstate, ExplainState *es)
 
static void show_ctescan_info (CteScanState *ctescanstate, ExplainState *es)
 
static void show_table_func_scan_info (TableFuncScanState *tscanstate, ExplainState *es)
 
static void show_recursive_union_info (RecursiveUnionState *rstate, ExplainState *es)
 
static void show_memoize_info (MemoizeState *mstate, List *ancestors, ExplainState *es)
 
static void show_hashagg_info (AggState *aggstate, ExplainState *es)
 
static void show_indexsearches_info (PlanState *planstate, ExplainState *es)
 
static void show_tidbitmap_info (BitmapHeapScanState *planstate, 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 bool peek_buffer_usage (ExplainState *es, const BufferUsage *usage)
 
static void show_buffer_usage (ExplainState *es, const BufferUsage *usage)
 
static void show_wal_usage (ExplainState *es, const WalUsage *usage)
 
static void show_memory_counters (ExplainState *es, const MemoryContextCounters *mem_counters)
 
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, List *ancestors, ExplainState *es)
 
static void ExplainMemberNodes (PlanState **planstates, int nplans, List *ancestors, ExplainState *es)
 
static void ExplainMissingMembers (int nplans, int nchildren, ExplainState *es)
 
static void ExplainSubPlans (List *plans, List *ancestors, const char *relationship, ExplainState *es)
 
static void ExplainCustomChildren (CustomScanState *css, List *ancestors, ExplainState *es)
 
static ExplainWorkersStateExplainCreateWorkersState (int num_workers)
 
static void ExplainOpenWorker (int n, ExplainState *es)
 
static void ExplainCloseWorker (int n, ExplainState *es)
 
static void ExplainFlushWorkersState (ExplainState *es)
 
void ExplainQuery (ParseState *pstate, ExplainStmt *stmt, ParamListInfo params, DestReceiver *dest)
 
TupleDesc ExplainResultDesc (ExplainStmt *stmt)
 
void standard_ExplainOneQuery (Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
 
void ExplainOneUtility (Node *utilityStmt, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
 
void ExplainOnePlan (PlannedStmt *plannedstmt, CachedPlan *cplan, CachedPlanSource *plansource, int query_index, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv, const instr_time *planduration, const BufferUsage *bufusage, const MemoryContextCounters *mem_counters)
 
static void ExplainPrintSettings (ExplainState *es)
 
void ExplainPrintPlan (ExplainState *es, QueryDesc *queryDesc)
 
void ExplainPrintTriggers (ExplainState *es, QueryDesc *queryDesc)
 
void ExplainPrintJITSummary (ExplainState *es, QueryDesc *queryDesc)
 
void ExplainQueryText (ExplainState *es, QueryDesc *queryDesc)
 
void ExplainQueryParameters (ExplainState *es, ParamListInfo params, int maxlen)
 
static bool plan_is_disabled (Plan *plan)
 
static void show_incremental_sort_group_info (IncrementalSortGroupInfo *groupInfo, const char *groupLabel, bool indent, ExplainState *es)
 

Variables

ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL
 
explain_get_index_name_hook_type explain_get_index_name_hook = NULL
 
explain_per_plan_hook_type explain_per_plan_hook = NULL
 
explain_per_node_hook_type explain_per_node_hook = NULL
 

Macro Definition Documentation

◆ BYTES_TO_KILOBYTES

#define BYTES_TO_KILOBYTES (   b)    (((b) + 1023) / 1024)

Definition at line 63 of file explain.c.

Function Documentation

◆ elapsed_time()

static double elapsed_time ( instr_time starttime)
static

Definition at line 1179 of file explain.c.

1180{
1181 instr_time endtime;
1182
1183 INSTR_TIME_SET_CURRENT(endtime);
1184 INSTR_TIME_SUBTRACT(endtime, *starttime);
1185 return INSTR_TIME_GET_DOUBLE(endtime);
1186}
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:122
#define INSTR_TIME_GET_DOUBLE(t)
Definition: instr_time.h:188
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:181

References INSTR_TIME_GET_DOUBLE, INSTR_TIME_SET_CURRENT, and INSTR_TIME_SUBTRACT.

Referenced by ExplainOnePlan(), and IsCheckpointOnSchedule().

◆ explain_get_index_name()

static const char * explain_get_index_name ( Oid  indexId)
static

Definition at line 4034 of file explain.c.

4035{
4036 const char *result;
4037
4039 result = (*explain_get_index_name_hook) (indexId);
4040 else
4041 result = NULL;
4042 if (result == NULL)
4043 {
4044 /* default behavior: look it up in the catalogs */
4045 result = get_rel_name(indexId);
4046 if (result == NULL)
4047 elog(ERROR, "cache lookup failed for index %u", indexId);
4048 }
4049 return result;
4050}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
explain_get_index_name_hook_type explain_get_index_name_hook
Definition: explain.c:53
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2011

References elog, ERROR, explain_get_index_name_hook, and get_rel_name().

Referenced by ExplainIndexScanDetails(), and ExplainNode().

◆ ExplainCloseWorker()

static void ExplainCloseWorker ( int  n,
ExplainState es 
)
static

Definition at line 4930 of file explain.c.

4931{
4932 ExplainWorkersState *wstate = es->workers_state;
4933
4934 Assert(wstate);
4935 Assert(n >= 0 && n < wstate->num_workers);
4936 Assert(wstate->worker_inited[n]);
4937
4938 /*
4939 * Save formatting state in case we do another ExplainOpenWorker(), then
4940 * pop the formatting stack.
4941 */
4942 ExplainSaveGroup(es, 2, &wstate->worker_state_save[n]);
4943
4944 /*
4945 * In TEXT format, if we didn't actually produce any output line(s) then
4946 * truncate off the partial line emitted by ExplainOpenWorker. (This is
4947 * to avoid bogus output if, say, show_buffer_usage chooses not to print
4948 * anything for the worker.) Also fix up the indent level.
4949 */
4950 if (es->format == EXPLAIN_FORMAT_TEXT)
4951 {
4952 while (es->str->len > 0 && es->str->data[es->str->len - 1] != '\n')
4953 es->str->data[--(es->str->len)] = '\0';
4954
4955 es->indent--;
4956 }
4957
4958 /* Restore prior output buffer pointer */
4959 es->str = wstate->prev_str;
4960}
void ExplainSaveGroup(ExplainState *es, int depth, int *state_save)
@ EXPLAIN_FORMAT_TEXT
Definition: explain_state.h:29
Assert(PointerIsAligned(start, uint64))
ExplainWorkersState * workers_state
Definition: explain_state.h:73
StringInfo str
Definition: explain_state.h:46
ExplainFormat format
Definition: explain_state.h:59

References Assert(), StringInfoData::data, EXPLAIN_FORMAT_TEXT, ExplainSaveGroup(), ExplainState::format, ExplainState::indent, StringInfoData::len, ExplainWorkersState::prev_str, ExplainState::str, ExplainWorkersState::worker_inited, ExplainWorkersState::worker_state_save, and ExplainState::workers_state.

Referenced by ExplainNode(), show_hashagg_info(), show_incremental_sort_info(), show_memoize_info(), show_sort_info(), and show_tidbitmap_info().

◆ ExplainCreateWorkersState()

static ExplainWorkersState * ExplainCreateWorkersState ( int  num_workers)
static

Definition at line 4851 of file explain.c.

4852{
4853 ExplainWorkersState *wstate;
4854
4855 wstate = (ExplainWorkersState *) palloc(sizeof(ExplainWorkersState));
4856 wstate->num_workers = num_workers;
4857 wstate->worker_inited = (bool *) palloc0(num_workers * sizeof(bool));
4858 wstate->worker_str = (StringInfoData *)
4859 palloc0(num_workers * sizeof(StringInfoData));
4860 wstate->worker_state_save = (int *) palloc(num_workers * sizeof(int));
4861 return wstate;
4862}
void * palloc0(Size size)
Definition: mcxt.c:1347
void * palloc(Size size)
Definition: mcxt.c:1317
StringInfoData * worker_str
Definition: explain_state.h:39

References ExplainWorkersState::num_workers, palloc(), palloc0(), ExplainWorkersState::worker_inited, ExplainWorkersState::worker_state_save, and ExplainWorkersState::worker_str.

Referenced by ExplainNode().

◆ ExplainCustomChildren()

static void ExplainCustomChildren ( CustomScanState css,
List ancestors,
ExplainState es 
)
static

Definition at line 4829 of file explain.c.

4830{
4831 ListCell *cell;
4832 const char *label =
4833 (list_length(css->custom_ps) != 1 ? "children" : "child");
4834
4835 foreach(cell, css->custom_ps)
4836 ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
4837}
static void ExplainNode(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
Definition: explain.c:1363
static char * label
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
List * custom_ps
Definition: execnodes.h:2128

References CustomScanState::custom_ps, ExplainNode(), label, lfirst, and list_length().

Referenced by ExplainNode().

◆ ExplainFlushWorkersState()

static void ExplainFlushWorkersState ( ExplainState es)
static

Definition at line 4966 of file explain.c.

4967{
4968 ExplainWorkersState *wstate = es->workers_state;
4969
4970 ExplainOpenGroup("Workers", "Workers", false, es);
4971 for (int i = 0; i < wstate->num_workers; i++)
4972 {
4973 if (wstate->worker_inited[i])
4974 {
4975 /* This must match previous ExplainOpenSetAsideGroup call */
4976 ExplainOpenGroup("Worker", NULL, true, es);
4978 ExplainCloseGroup("Worker", NULL, true, es);
4979
4980 pfree(wstate->worker_str[i].data);
4981 }
4982 }
4983 ExplainCloseGroup("Workers", "Workers", false, es);
4984
4985 pfree(wstate->worker_inited);
4986 pfree(wstate->worker_str);
4987 pfree(wstate->worker_state_save);
4988 pfree(wstate);
4989}
void ExplainOpenGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
void ExplainCloseGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
int i
Definition: isn.c:74
void pfree(void *pointer)
Definition: mcxt.c:1524
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230

References appendStringInfoString(), StringInfoData::data, ExplainCloseGroup(), ExplainOpenGroup(), i, ExplainWorkersState::num_workers, pfree(), ExplainState::str, ExplainWorkersState::worker_inited, ExplainWorkersState::worker_state_save, ExplainWorkersState::worker_str, and ExplainState::workers_state.

Referenced by ExplainNode().

◆ ExplainIndexScanDetails()

static void ExplainIndexScanDetails ( Oid  indexid,
ScanDirection  indexorderdir,
ExplainState es 
)
static

Definition at line 4336 of file explain.c.

4338{
4339 const char *indexname = explain_get_index_name(indexid);
4340
4341 if (es->format == EXPLAIN_FORMAT_TEXT)
4342 {
4343 if (ScanDirectionIsBackward(indexorderdir))
4344 appendStringInfoString(es->str, " Backward");
4345 appendStringInfo(es->str, " using %s", quote_identifier(indexname));
4346 }
4347 else
4348 {
4349 const char *scandir;
4350
4351 switch (indexorderdir)
4352 {
4354 scandir = "Backward";
4355 break;
4357 scandir = "Forward";
4358 break;
4359 default:
4360 scandir = "???";
4361 break;
4362 }
4363 ExplainPropertyText("Scan Direction", scandir, es);
4364 ExplainPropertyText("Index Name", indexname, es);
4365 }
4366}
static const char * explain_get_index_name(Oid indexId)
Definition: explain.c:4034
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13019
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:50
@ BackwardScanDirection
Definition: sdir.h:26
@ ForwardScanDirection
Definition: sdir.h:28
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:145

References appendStringInfo(), appendStringInfoString(), BackwardScanDirection, EXPLAIN_FORMAT_TEXT, explain_get_index_name(), ExplainPropertyText(), ExplainState::format, ForwardScanDirection, quote_identifier(), ScanDirectionIsBackward, and ExplainState::str.

Referenced by ExplainNode().

◆ ExplainMemberNodes()

static void ExplainMemberNodes ( PlanState **  planstates,
int  nplans,
List ancestors,
ExplainState es 
)
static

Definition at line 4754 of file explain.c.

4756{
4757 int j;
4758
4759 for (j = 0; j < nplans; j++)
4760 ExplainNode(planstates[j], ancestors,
4761 "Member", NULL, es);
4762}
int j
Definition: isn.c:75

References ExplainNode(), and j.

Referenced by ExplainNode().

◆ ExplainMissingMembers()

static void ExplainMissingMembers ( int  nplans,
int  nchildren,
ExplainState es 
)
static

Definition at line 4772 of file explain.c.

4773{
4774 if (nplans < nchildren || es->format != EXPLAIN_FORMAT_TEXT)
4775 ExplainPropertyInteger("Subplans Removed", NULL,
4776 nchildren - nplans, es);
4777}
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
static char format

References EXPLAIN_FORMAT_TEXT, ExplainPropertyInteger(), and format.

Referenced by ExplainNode().

◆ ExplainModifyTarget()

static void ExplainModifyTarget ( ModifyTable plan,
ExplainState es 
)
static

Definition at line 4385 of file explain.c.

4386{
4387 ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
4388}
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
Definition: explain.c:4394
#define plan(x)
Definition: pg_regress.c:161

References ExplainTargetRel(), and plan.

Referenced by ExplainNode().

◆ ExplainNode()

static void ExplainNode ( PlanState planstate,
List ancestors,
const char *  relationship,
const char *  plan_name,
ExplainState es 
)
static

Definition at line 1363 of file explain.c.

1366{
1367 Plan *plan = planstate->plan;
1368 const char *pname; /* node type name for text output */
1369 const char *sname; /* node type name for non-text output */
1370 const char *strategy = NULL;
1371 const char *partialmode = NULL;
1372 const char *operation = NULL;
1373 const char *custom_name = NULL;
1374 ExplainWorkersState *save_workers_state = es->workers_state;
1375 int save_indent = es->indent;
1376 bool haschildren;
1377 bool isdisabled;
1378
1379 /*
1380 * Prepare per-worker output buffers, if needed. We'll append the data in
1381 * these to the main output string further down.
1382 */
1383 if (planstate->worker_instrument && es->analyze && !es->hide_workers)
1385 else
1386 es->workers_state = NULL;
1387
1388 /* Identify plan node type, and print generic details */
1389 switch (nodeTag(plan))
1390 {
1391 case T_Result:
1392 pname = sname = "Result";
1393 break;
1394 case T_ProjectSet:
1395 pname = sname = "ProjectSet";
1396 break;
1397 case T_ModifyTable:
1398 sname = "ModifyTable";
1399 switch (((ModifyTable *) plan)->operation)
1400 {
1401 case CMD_INSERT:
1402 pname = operation = "Insert";
1403 break;
1404 case CMD_UPDATE:
1405 pname = operation = "Update";
1406 break;
1407 case CMD_DELETE:
1408 pname = operation = "Delete";
1409 break;
1410 case CMD_MERGE:
1411 pname = operation = "Merge";
1412 break;
1413 default:
1414 pname = "???";
1415 break;
1416 }
1417 break;
1418 case T_Append:
1419 pname = sname = "Append";
1420 break;
1421 case T_MergeAppend:
1422 pname = sname = "Merge Append";
1423 break;
1424 case T_RecursiveUnion:
1425 pname = sname = "Recursive Union";
1426 break;
1427 case T_BitmapAnd:
1428 pname = sname = "BitmapAnd";
1429 break;
1430 case T_BitmapOr:
1431 pname = sname = "BitmapOr";
1432 break;
1433 case T_NestLoop:
1434 pname = sname = "Nested Loop";
1435 break;
1436 case T_MergeJoin:
1437 pname = "Merge"; /* "Join" gets added by jointype switch */
1438 sname = "Merge Join";
1439 break;
1440 case T_HashJoin:
1441 pname = "Hash"; /* "Join" gets added by jointype switch */
1442 sname = "Hash Join";
1443 break;
1444 case T_SeqScan:
1445 pname = sname = "Seq Scan";
1446 break;
1447 case T_SampleScan:
1448 pname = sname = "Sample Scan";
1449 break;
1450 case T_Gather:
1451 pname = sname = "Gather";
1452 break;
1453 case T_GatherMerge:
1454 pname = sname = "Gather Merge";
1455 break;
1456 case T_IndexScan:
1457 pname = sname = "Index Scan";
1458 break;
1459 case T_IndexOnlyScan:
1460 pname = sname = "Index Only Scan";
1461 break;
1462 case T_BitmapIndexScan:
1463 pname = sname = "Bitmap Index Scan";
1464 break;
1465 case T_BitmapHeapScan:
1466 pname = sname = "Bitmap Heap Scan";
1467 break;
1468 case T_TidScan:
1469 pname = sname = "Tid Scan";
1470 break;
1471 case T_TidRangeScan:
1472 pname = sname = "Tid Range Scan";
1473 break;
1474 case T_SubqueryScan:
1475 pname = sname = "Subquery Scan";
1476 break;
1477 case T_FunctionScan:
1478 pname = sname = "Function Scan";
1479 break;
1480 case T_TableFuncScan:
1481 pname = sname = "Table Function Scan";
1482 break;
1483 case T_ValuesScan:
1484 pname = sname = "Values Scan";
1485 break;
1486 case T_CteScan:
1487 pname = sname = "CTE Scan";
1488 break;
1489 case T_NamedTuplestoreScan:
1490 pname = sname = "Named Tuplestore Scan";
1491 break;
1492 case T_WorkTableScan:
1493 pname = sname = "WorkTable Scan";
1494 break;
1495 case T_ForeignScan:
1496 sname = "Foreign Scan";
1497 switch (((ForeignScan *) plan)->operation)
1498 {
1499 case CMD_SELECT:
1500 pname = "Foreign Scan";
1501 operation = "Select";
1502 break;
1503 case CMD_INSERT:
1504 pname = "Foreign Insert";
1505 operation = "Insert";
1506 break;
1507 case CMD_UPDATE:
1508 pname = "Foreign Update";
1509 operation = "Update";
1510 break;
1511 case CMD_DELETE:
1512 pname = "Foreign Delete";
1513 operation = "Delete";
1514 break;
1515 default:
1516 pname = "???";
1517 break;
1518 }
1519 break;
1520 case T_CustomScan:
1521 sname = "Custom Scan";
1522 custom_name = ((CustomScan *) plan)->methods->CustomName;
1523 if (custom_name)
1524 pname = psprintf("Custom Scan (%s)", custom_name);
1525 else
1526 pname = sname;
1527 break;
1528 case T_Material:
1529 pname = sname = "Materialize";
1530 break;
1531 case T_Memoize:
1532 pname = sname = "Memoize";
1533 break;
1534 case T_Sort:
1535 pname = sname = "Sort";
1536 break;
1537 case T_IncrementalSort:
1538 pname = sname = "Incremental Sort";
1539 break;
1540 case T_Group:
1541 pname = sname = "Group";
1542 break;
1543 case T_Agg:
1544 {
1545 Agg *agg = (Agg *) plan;
1546
1547 sname = "Aggregate";
1548 switch (agg->aggstrategy)
1549 {
1550 case AGG_PLAIN:
1551 pname = "Aggregate";
1552 strategy = "Plain";
1553 break;
1554 case AGG_SORTED:
1555 pname = "GroupAggregate";
1556 strategy = "Sorted";
1557 break;
1558 case AGG_HASHED:
1559 pname = "HashAggregate";
1560 strategy = "Hashed";
1561 break;
1562 case AGG_MIXED:
1563 pname = "MixedAggregate";
1564 strategy = "Mixed";
1565 break;
1566 default:
1567 pname = "Aggregate ???";
1568 strategy = "???";
1569 break;
1570 }
1571
1573 {
1574 partialmode = "Partial";
1575 pname = psprintf("%s %s", partialmode, pname);
1576 }
1577 else if (DO_AGGSPLIT_COMBINE(agg->aggsplit))
1578 {
1579 partialmode = "Finalize";
1580 pname = psprintf("%s %s", partialmode, pname);
1581 }
1582 else
1583 partialmode = "Simple";
1584 }
1585 break;
1586 case T_WindowAgg:
1587 pname = sname = "WindowAgg";
1588 break;
1589 case T_Unique:
1590 pname = sname = "Unique";
1591 break;
1592 case T_SetOp:
1593 sname = "SetOp";
1594 switch (((SetOp *) plan)->strategy)
1595 {
1596 case SETOP_SORTED:
1597 pname = "SetOp";
1598 strategy = "Sorted";
1599 break;
1600 case SETOP_HASHED:
1601 pname = "HashSetOp";
1602 strategy = "Hashed";
1603 break;
1604 default:
1605 pname = "SetOp ???";
1606 strategy = "???";
1607 break;
1608 }
1609 break;
1610 case T_LockRows:
1611 pname = sname = "LockRows";
1612 break;
1613 case T_Limit:
1614 pname = sname = "Limit";
1615 break;
1616 case T_Hash:
1617 pname = sname = "Hash";
1618 break;
1619 default:
1620 pname = sname = "???";
1621 break;
1622 }
1623
1624 ExplainOpenGroup("Plan",
1625 relationship ? NULL : "Plan",
1626 true, es);
1627
1628 if (es->format == EXPLAIN_FORMAT_TEXT)
1629 {
1630 if (plan_name)
1631 {
1633 appendStringInfo(es->str, "%s\n", plan_name);
1634 es->indent++;
1635 }
1636 if (es->indent)
1637 {
1639 appendStringInfoString(es->str, "-> ");
1640 es->indent += 2;
1641 }
1642 if (plan->parallel_aware)
1643 appendStringInfoString(es->str, "Parallel ");
1644 if (plan->async_capable)
1645 appendStringInfoString(es->str, "Async ");
1646 appendStringInfoString(es->str, pname);
1647 es->indent++;
1648 }
1649 else
1650 {
1651 ExplainPropertyText("Node Type", sname, es);
1652 if (strategy)
1653 ExplainPropertyText("Strategy", strategy, es);
1654 if (partialmode)
1655 ExplainPropertyText("Partial Mode", partialmode, es);
1656 if (operation)
1657 ExplainPropertyText("Operation", operation, es);
1658 if (relationship)
1659 ExplainPropertyText("Parent Relationship", relationship, es);
1660 if (plan_name)
1661 ExplainPropertyText("Subplan Name", plan_name, es);
1662 if (custom_name)
1663 ExplainPropertyText("Custom Plan Provider", custom_name, es);
1664 ExplainPropertyBool("Parallel Aware", plan->parallel_aware, es);
1665 ExplainPropertyBool("Async Capable", plan->async_capable, es);
1666 }
1667
1668 switch (nodeTag(plan))
1669 {
1670 case T_SeqScan:
1671 case T_SampleScan:
1672 case T_BitmapHeapScan:
1673 case T_TidScan:
1674 case T_TidRangeScan:
1675 case T_SubqueryScan:
1676 case T_FunctionScan:
1677 case T_TableFuncScan:
1678 case T_ValuesScan:
1679 case T_CteScan:
1680 case T_WorkTableScan:
1681 ExplainScanTarget((Scan *) plan, es);
1682 break;
1683 case T_ForeignScan:
1684 case T_CustomScan:
1685 if (((Scan *) plan)->scanrelid > 0)
1686 ExplainScanTarget((Scan *) plan, es);
1687 break;
1688 case T_IndexScan:
1689 {
1690 IndexScan *indexscan = (IndexScan *) plan;
1691
1693 indexscan->indexorderdir,
1694 es);
1695 ExplainScanTarget((Scan *) indexscan, es);
1696 }
1697 break;
1698 case T_IndexOnlyScan:
1699 {
1700 IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
1701
1702 ExplainIndexScanDetails(indexonlyscan->indexid,
1703 indexonlyscan->indexorderdir,
1704 es);
1705 ExplainScanTarget((Scan *) indexonlyscan, es);
1706 }
1707 break;
1708 case T_BitmapIndexScan:
1709 {
1710 BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
1711 const char *indexname =
1712 explain_get_index_name(bitmapindexscan->indexid);
1713
1714 if (es->format == EXPLAIN_FORMAT_TEXT)
1715 appendStringInfo(es->str, " on %s",
1716 quote_identifier(indexname));
1717 else
1718 ExplainPropertyText("Index Name", indexname, es);
1719 }
1720 break;
1721 case T_ModifyTable:
1723 break;
1724 case T_NestLoop:
1725 case T_MergeJoin:
1726 case T_HashJoin:
1727 {
1728 const char *jointype;
1729
1730 switch (((Join *) plan)->jointype)
1731 {
1732 case JOIN_INNER:
1733 jointype = "Inner";
1734 break;
1735 case JOIN_LEFT:
1736 jointype = "Left";
1737 break;
1738 case JOIN_FULL:
1739 jointype = "Full";
1740 break;
1741 case JOIN_RIGHT:
1742 jointype = "Right";
1743 break;
1744 case JOIN_SEMI:
1745 jointype = "Semi";
1746 break;
1747 case JOIN_ANTI:
1748 jointype = "Anti";
1749 break;
1750 case JOIN_RIGHT_SEMI:
1751 jointype = "Right Semi";
1752 break;
1753 case JOIN_RIGHT_ANTI:
1754 jointype = "Right Anti";
1755 break;
1756 default:
1757 jointype = "???";
1758 break;
1759 }
1760 if (es->format == EXPLAIN_FORMAT_TEXT)
1761 {
1762 /*
1763 * For historical reasons, the join type is interpolated
1764 * into the node type name...
1765 */
1766 if (((Join *) plan)->jointype != JOIN_INNER)
1767 appendStringInfo(es->str, " %s Join", jointype);
1768 else if (!IsA(plan, NestLoop))
1769 appendStringInfoString(es->str, " Join");
1770 }
1771 else
1772 ExplainPropertyText("Join Type", jointype, es);
1773 }
1774 break;
1775 case T_SetOp:
1776 {
1777 const char *setopcmd;
1778
1779 switch (((SetOp *) plan)->cmd)
1780 {
1781 case SETOPCMD_INTERSECT:
1782 setopcmd = "Intersect";
1783 break;
1785 setopcmd = "Intersect All";
1786 break;
1787 case SETOPCMD_EXCEPT:
1788 setopcmd = "Except";
1789 break;
1791 setopcmd = "Except All";
1792 break;
1793 default:
1794 setopcmd = "???";
1795 break;
1796 }
1797 if (es->format == EXPLAIN_FORMAT_TEXT)
1798 appendStringInfo(es->str, " %s", setopcmd);
1799 else
1800 ExplainPropertyText("Command", setopcmd, es);
1801 }
1802 break;
1803 default:
1804 break;
1805 }
1806
1807 if (es->costs)
1808 {
1809 if (es->format == EXPLAIN_FORMAT_TEXT)
1810 {
1811 appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
1812 plan->startup_cost, plan->total_cost,
1813 plan->plan_rows, plan->plan_width);
1814 }
1815 else
1816 {
1817 ExplainPropertyFloat("Startup Cost", NULL, plan->startup_cost,
1818 2, es);
1819 ExplainPropertyFloat("Total Cost", NULL, plan->total_cost,
1820 2, es);
1821 ExplainPropertyFloat("Plan Rows", NULL, plan->plan_rows,
1822 0, es);
1823 ExplainPropertyInteger("Plan Width", NULL, plan->plan_width,
1824 es);
1825 }
1826 }
1827
1828 /*
1829 * We have to forcibly clean up the instrumentation state because we
1830 * haven't done ExecutorEnd yet. This is pretty grotty ...
1831 *
1832 * Note: contrib/auto_explain could cause instrumentation to be set up
1833 * even though we didn't ask for it here. Be careful not to print any
1834 * instrumentation results the user didn't ask for. But we do the
1835 * InstrEndLoop call anyway, if possible, to reduce the number of cases
1836 * auto_explain has to contend with.
1837 */
1838 if (planstate->instrument)
1839 InstrEndLoop(planstate->instrument);
1840
1841 if (es->analyze &&
1842 planstate->instrument && planstate->instrument->nloops > 0)
1843 {
1844 double nloops = planstate->instrument->nloops;
1845 double startup_ms = 1000.0 * planstate->instrument->startup / nloops;
1846 double total_ms = 1000.0 * planstate->instrument->total / nloops;
1847 double rows = planstate->instrument->ntuples / nloops;
1848
1849 if (es->format == EXPLAIN_FORMAT_TEXT)
1850 {
1851 appendStringInfo(es->str, " (actual ");
1852
1853 if (es->timing)
1854 appendStringInfo(es->str, "time=%.3f..%.3f ", startup_ms, total_ms);
1855
1856 appendStringInfo(es->str, "rows=%.2f loops=%.0f)", rows, nloops);
1857 }
1858 else
1859 {
1860 if (es->timing)
1861 {
1862 ExplainPropertyFloat("Actual Startup Time", "ms", startup_ms,
1863 3, es);
1864 ExplainPropertyFloat("Actual Total Time", "ms", total_ms,
1865 3, es);
1866 }
1867 ExplainPropertyFloat("Actual Rows", NULL, rows, 2, es);
1868 ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
1869 }
1870 }
1871 else if (es->analyze)
1872 {
1873 if (es->format == EXPLAIN_FORMAT_TEXT)
1874 appendStringInfoString(es->str, " (never executed)");
1875 else
1876 {
1877 if (es->timing)
1878 {
1879 ExplainPropertyFloat("Actual Startup Time", "ms", 0.0, 3, es);
1880 ExplainPropertyFloat("Actual Total Time", "ms", 0.0, 3, es);
1881 }
1882 ExplainPropertyFloat("Actual Rows", NULL, 0.0, 0, es);
1883 ExplainPropertyFloat("Actual Loops", NULL, 0.0, 0, es);
1884 }
1885 }
1886
1887 /* in text format, first line ends here */
1888 if (es->format == EXPLAIN_FORMAT_TEXT)
1889 appendStringInfoChar(es->str, '\n');
1890
1891
1892 isdisabled = plan_is_disabled(plan);
1893 if (es->format != EXPLAIN_FORMAT_TEXT || isdisabled)
1894 ExplainPropertyBool("Disabled", isdisabled, es);
1895
1896 /* prepare per-worker general execution details */
1897 if (es->workers_state && es->verbose)
1898 {
1900
1901 for (int n = 0; n < w->num_workers; n++)
1902 {
1903 Instrumentation *instrument = &w->instrument[n];
1904 double nloops = instrument->nloops;
1905 double startup_ms;
1906 double total_ms;
1907 double rows;
1908
1909 if (nloops <= 0)
1910 continue;
1911 startup_ms = 1000.0 * instrument->startup / nloops;
1912 total_ms = 1000.0 * instrument->total / nloops;
1913 rows = instrument->ntuples / nloops;
1914
1915 ExplainOpenWorker(n, es);
1916
1917 if (es->format == EXPLAIN_FORMAT_TEXT)
1918 {
1920 appendStringInfo(es->str, "actual ");
1921 if (es->timing)
1922 appendStringInfo(es->str, "time=%.3f..%.3f ", startup_ms, total_ms);
1923
1924 appendStringInfo(es->str, "rows=%.2f loops=%.0f\n", rows, nloops);
1925 }
1926 else
1927 {
1928 if (es->timing)
1929 {
1930 ExplainPropertyFloat("Actual Startup Time", "ms",
1931 startup_ms, 3, es);
1932 ExplainPropertyFloat("Actual Total Time", "ms",
1933 total_ms, 3, es);
1934 }
1935
1936 ExplainPropertyFloat("Actual Rows", NULL, rows, 2, es);
1937 ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
1938 }
1939
1940 ExplainCloseWorker(n, es);
1941 }
1942 }
1943
1944 /* target list */
1945 if (es->verbose)
1946 show_plan_tlist(planstate, ancestors, es);
1947
1948 /* unique join */
1949 switch (nodeTag(plan))
1950 {
1951 case T_NestLoop:
1952 case T_MergeJoin:
1953 case T_HashJoin:
1954 /* try not to be too chatty about this in text mode */
1955 if (es->format != EXPLAIN_FORMAT_TEXT ||
1956 (es->verbose && ((Join *) plan)->inner_unique))
1957 ExplainPropertyBool("Inner Unique",
1958 ((Join *) plan)->inner_unique,
1959 es);
1960 break;
1961 default:
1962 break;
1963 }
1964
1965 /* quals, sort keys, etc */
1966 switch (nodeTag(plan))
1967 {
1968 case T_IndexScan:
1969 show_scan_qual(((IndexScan *) plan)->indexqualorig,
1970 "Index Cond", planstate, ancestors, es);
1971 if (((IndexScan *) plan)->indexqualorig)
1972 show_instrumentation_count("Rows Removed by Index Recheck", 2,
1973 planstate, es);
1974 show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
1975 "Order By", planstate, ancestors, es);
1976 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1977 if (plan->qual)
1978 show_instrumentation_count("Rows Removed by Filter", 1,
1979 planstate, es);
1980 show_indexsearches_info(planstate, es);
1981 break;
1982 case T_IndexOnlyScan:
1983 show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
1984 "Index Cond", planstate, ancestors, es);
1985 if (((IndexOnlyScan *) plan)->recheckqual)
1986 show_instrumentation_count("Rows Removed by Index Recheck", 2,
1987 planstate, es);
1988 show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
1989 "Order By", planstate, ancestors, es);
1990 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1991 if (plan->qual)
1992 show_instrumentation_count("Rows Removed by Filter", 1,
1993 planstate, es);
1994 if (es->analyze)
1995 ExplainPropertyFloat("Heap Fetches", NULL,
1996 planstate->instrument->ntuples2, 0, es);
1997 show_indexsearches_info(planstate, es);
1998 break;
1999 case T_BitmapIndexScan:
2000 show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
2001 "Index Cond", planstate, ancestors, es);
2002 show_indexsearches_info(planstate, es);
2003 break;
2004 case T_BitmapHeapScan:
2005 show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
2006 "Recheck Cond", planstate, ancestors, es);
2007 if (((BitmapHeapScan *) plan)->bitmapqualorig)
2008 show_instrumentation_count("Rows Removed by Index Recheck", 2,
2009 planstate, es);
2010 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2011 if (plan->qual)
2012 show_instrumentation_count("Rows Removed by Filter", 1,
2013 planstate, es);
2014 show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
2015 break;
2016 case T_SampleScan:
2017 show_tablesample(((SampleScan *) plan)->tablesample,
2018 planstate, ancestors, es);
2019 /* fall through to print additional fields the same as SeqScan */
2020 /* FALLTHROUGH */
2021 case T_SeqScan:
2022 case T_ValuesScan:
2023 case T_CteScan:
2024 case T_NamedTuplestoreScan:
2025 case T_WorkTableScan:
2026 case T_SubqueryScan:
2027 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2028 if (plan->qual)
2029 show_instrumentation_count("Rows Removed by Filter", 1,
2030 planstate, es);
2031 if (IsA(plan, CteScan))
2032 show_ctescan_info(castNode(CteScanState, planstate), es);
2033 break;
2034 case T_Gather:
2035 {
2036 Gather *gather = (Gather *) plan;
2037
2038 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2039 if (plan->qual)
2040 show_instrumentation_count("Rows Removed by Filter", 1,
2041 planstate, es);
2042 ExplainPropertyInteger("Workers Planned", NULL,
2043 gather->num_workers, es);
2044
2045 if (es->analyze)
2046 {
2047 int nworkers;
2048
2049 nworkers = ((GatherState *) planstate)->nworkers_launched;
2050 ExplainPropertyInteger("Workers Launched", NULL,
2051 nworkers, es);
2052 }
2053
2054 if (gather->single_copy || es->format != EXPLAIN_FORMAT_TEXT)
2055 ExplainPropertyBool("Single Copy", gather->single_copy, es);
2056 }
2057 break;
2058 case T_GatherMerge:
2059 {
2060 GatherMerge *gm = (GatherMerge *) plan;
2061
2062 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2063 if (plan->qual)
2064 show_instrumentation_count("Rows Removed by Filter", 1,
2065 planstate, es);
2066 ExplainPropertyInteger("Workers Planned", NULL,
2067 gm->num_workers, es);
2068
2069 if (es->analyze)
2070 {
2071 int nworkers;
2072
2073 nworkers = ((GatherMergeState *) planstate)->nworkers_launched;
2074 ExplainPropertyInteger("Workers Launched", NULL,
2075 nworkers, es);
2076 }
2077 }
2078 break;
2079 case T_FunctionScan:
2080 if (es->verbose)
2081 {
2082 List *fexprs = NIL;
2083 ListCell *lc;
2084
2085 foreach(lc, ((FunctionScan *) plan)->functions)
2086 {
2087 RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
2088
2089 fexprs = lappend(fexprs, rtfunc->funcexpr);
2090 }
2091 /* We rely on show_expression to insert commas as needed */
2092 show_expression((Node *) fexprs,
2093 "Function Call", planstate, ancestors,
2094 es->verbose, es);
2095 }
2096 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2097 if (plan->qual)
2098 show_instrumentation_count("Rows Removed by Filter", 1,
2099 planstate, es);
2100 break;
2101 case T_TableFuncScan:
2102 if (es->verbose)
2103 {
2104 TableFunc *tablefunc = ((TableFuncScan *) plan)->tablefunc;
2105
2106 show_expression((Node *) tablefunc,
2107 "Table Function Call", planstate, ancestors,
2108 es->verbose, es);
2109 }
2110 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2111 if (plan->qual)
2112 show_instrumentation_count("Rows Removed by Filter", 1,
2113 planstate, es);
2115 planstate), es);
2116 break;
2117 case T_TidScan:
2118 {
2119 /*
2120 * The tidquals list has OR semantics, so be sure to show it
2121 * as an OR condition.
2122 */
2123 List *tidquals = ((TidScan *) plan)->tidquals;
2124
2125 if (list_length(tidquals) > 1)
2126 tidquals = list_make1(make_orclause(tidquals));
2127 show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
2128 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2129 if (plan->qual)
2130 show_instrumentation_count("Rows Removed by Filter", 1,
2131 planstate, es);
2132 }
2133 break;
2134 case T_TidRangeScan:
2135 {
2136 /*
2137 * The tidrangequals list has AND semantics, so be sure to
2138 * show it as an AND condition.
2139 */
2140 List *tidquals = ((TidRangeScan *) plan)->tidrangequals;
2141
2142 if (list_length(tidquals) > 1)
2143 tidquals = list_make1(make_andclause(tidquals));
2144 show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
2145 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2146 if (plan->qual)
2147 show_instrumentation_count("Rows Removed by Filter", 1,
2148 planstate, es);
2149 }
2150 break;
2151 case T_ForeignScan:
2152 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2153 if (plan->qual)
2154 show_instrumentation_count("Rows Removed by Filter", 1,
2155 planstate, es);
2156 show_foreignscan_info((ForeignScanState *) planstate, es);
2157 break;
2158 case T_CustomScan:
2159 {
2160 CustomScanState *css = (CustomScanState *) planstate;
2161
2162 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2163 if (plan->qual)
2164 show_instrumentation_count("Rows Removed by Filter", 1,
2165 planstate, es);
2166 if (css->methods->ExplainCustomScan)
2167 css->methods->ExplainCustomScan(css, ancestors, es);
2168 }
2169 break;
2170 case T_NestLoop:
2171 show_upper_qual(((NestLoop *) plan)->join.joinqual,
2172 "Join Filter", planstate, ancestors, es);
2173 if (((NestLoop *) plan)->join.joinqual)
2174 show_instrumentation_count("Rows Removed by Join Filter", 1,
2175 planstate, es);
2176 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2177 if (plan->qual)
2178 show_instrumentation_count("Rows Removed by Filter", 2,
2179 planstate, es);
2180 break;
2181 case T_MergeJoin:
2182 show_upper_qual(((MergeJoin *) plan)->mergeclauses,
2183 "Merge Cond", planstate, ancestors, es);
2184 show_upper_qual(((MergeJoin *) plan)->join.joinqual,
2185 "Join Filter", planstate, ancestors, es);
2186 if (((MergeJoin *) plan)->join.joinqual)
2187 show_instrumentation_count("Rows Removed by Join Filter", 1,
2188 planstate, es);
2189 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2190 if (plan->qual)
2191 show_instrumentation_count("Rows Removed by Filter", 2,
2192 planstate, es);
2193 break;
2194 case T_HashJoin:
2195 show_upper_qual(((HashJoin *) plan)->hashclauses,
2196 "Hash Cond", planstate, ancestors, es);
2197 show_upper_qual(((HashJoin *) plan)->join.joinqual,
2198 "Join Filter", planstate, ancestors, es);
2199 if (((HashJoin *) plan)->join.joinqual)
2200 show_instrumentation_count("Rows Removed by Join Filter", 1,
2201 planstate, es);
2202 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2203 if (plan->qual)
2204 show_instrumentation_count("Rows Removed by Filter", 2,
2205 planstate, es);
2206 break;
2207 case T_Agg:
2208 show_agg_keys(castNode(AggState, planstate), ancestors, es);
2209 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2210 show_hashagg_info((AggState *) planstate, es);
2211 if (plan->qual)
2212 show_instrumentation_count("Rows Removed by Filter", 1,
2213 planstate, es);
2214 break;
2215 case T_WindowAgg:
2216 show_window_def(castNode(WindowAggState, planstate), ancestors, es);
2217 show_upper_qual(((WindowAgg *) plan)->runConditionOrig,
2218 "Run Condition", planstate, ancestors, es);
2219 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2220 if (plan->qual)
2221 show_instrumentation_count("Rows Removed by Filter", 1,
2222 planstate, es);
2224 break;
2225 case T_Group:
2226 show_group_keys(castNode(GroupState, planstate), ancestors, es);
2227 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2228 if (plan->qual)
2229 show_instrumentation_count("Rows Removed by Filter", 1,
2230 planstate, es);
2231 break;
2232 case T_Sort:
2233 show_sort_keys(castNode(SortState, planstate), ancestors, es);
2234 show_sort_info(castNode(SortState, planstate), es);
2235 break;
2236 case T_IncrementalSort:
2238 ancestors, es);
2240 es);
2241 break;
2242 case T_MergeAppend:
2244 ancestors, es);
2245 break;
2246 case T_Result:
2247 show_upper_qual((List *) ((Result *) plan)->resconstantqual,
2248 "One-Time Filter", planstate, ancestors, es);
2249 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2250 if (plan->qual)
2251 show_instrumentation_count("Rows Removed by Filter", 1,
2252 planstate, es);
2253 break;
2254 case T_ModifyTable:
2255 show_modifytable_info(castNode(ModifyTableState, planstate), ancestors,
2256 es);
2257 break;
2258 case T_Hash:
2259 show_hash_info(castNode(HashState, planstate), es);
2260 break;
2261 case T_Material:
2262 show_material_info(castNode(MaterialState, planstate), es);
2263 break;
2264 case T_Memoize:
2265 show_memoize_info(castNode(MemoizeState, planstate), ancestors,
2266 es);
2267 break;
2268 case T_RecursiveUnion:
2270 planstate), es);
2271 break;
2272 default:
2273 break;
2274 }
2275
2276 /*
2277 * Prepare per-worker JIT instrumentation. As with the overall JIT
2278 * summary, this is printed only if printing costs is enabled.
2279 */
2280 if (es->workers_state && es->costs && es->verbose)
2281 {
2283
2284 if (w)
2285 {
2286 for (int n = 0; n < w->num_workers; n++)
2287 {
2288 ExplainOpenWorker(n, es);
2289 ExplainPrintJIT(es, planstate->state->es_jit_flags,
2290 &w->jit_instr[n]);
2291 ExplainCloseWorker(n, es);
2292 }
2293 }
2294 }
2295
2296 /* Show buffer/WAL usage */
2297 if (es->buffers && planstate->instrument)
2298 show_buffer_usage(es, &planstate->instrument->bufusage);
2299 if (es->wal && planstate->instrument)
2300 show_wal_usage(es, &planstate->instrument->walusage);
2301
2302 /* Prepare per-worker buffer/WAL usage */
2303 if (es->workers_state && (es->buffers || es->wal) && es->verbose)
2304 {
2306
2307 for (int n = 0; n < w->num_workers; n++)
2308 {
2309 Instrumentation *instrument = &w->instrument[n];
2310 double nloops = instrument->nloops;
2311
2312 if (nloops <= 0)
2313 continue;
2314
2315 ExplainOpenWorker(n, es);
2316 if (es->buffers)
2317 show_buffer_usage(es, &instrument->bufusage);
2318 if (es->wal)
2319 show_wal_usage(es, &instrument->walusage);
2320 ExplainCloseWorker(n, es);
2321 }
2322 }
2323
2324 /* Show per-worker details for this plan node, then pop that stack */
2325 if (es->workers_state)
2327 es->workers_state = save_workers_state;
2328
2329 /* Allow plugins to print additional information */
2331 (*explain_per_node_hook) (planstate, ancestors, relationship,
2332 plan_name, es);
2333
2334 /*
2335 * If partition pruning was done during executor initialization, the
2336 * number of child plans we'll display below will be less than the number
2337 * of subplans that was specified in the plan. To make this a bit less
2338 * mysterious, emit an indication that this happened. Note that this
2339 * field is emitted now because we want it to be a property of the parent
2340 * node; it *cannot* be emitted within the Plans sub-node we'll open next.
2341 */
2342 switch (nodeTag(plan))
2343 {
2344 case T_Append:
2345 ExplainMissingMembers(((AppendState *) planstate)->as_nplans,
2346 list_length(((Append *) plan)->appendplans),
2347 es);
2348 break;
2349 case T_MergeAppend:
2350 ExplainMissingMembers(((MergeAppendState *) planstate)->ms_nplans,
2351 list_length(((MergeAppend *) plan)->mergeplans),
2352 es);
2353 break;
2354 default:
2355 break;
2356 }
2357
2358 /* Get ready to display the child plans */
2359 haschildren = planstate->initPlan ||
2360 outerPlanState(planstate) ||
2361 innerPlanState(planstate) ||
2362 IsA(plan, Append) ||
2363 IsA(plan, MergeAppend) ||
2364 IsA(plan, BitmapAnd) ||
2365 IsA(plan, BitmapOr) ||
2366 IsA(plan, SubqueryScan) ||
2367 (IsA(planstate, CustomScanState) &&
2368 ((CustomScanState *) planstate)->custom_ps != NIL) ||
2369 planstate->subPlan;
2370 if (haschildren)
2371 {
2372 ExplainOpenGroup("Plans", "Plans", false, es);
2373 /* Pass current Plan as head of ancestors list for children */
2374 ancestors = lcons(plan, ancestors);
2375 }
2376
2377 /* initPlan-s */
2378 if (planstate->initPlan)
2379 ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
2380
2381 /* lefttree */
2382 if (outerPlanState(planstate))
2383 ExplainNode(outerPlanState(planstate), ancestors,
2384 "Outer", NULL, es);
2385
2386 /* righttree */
2387 if (innerPlanState(planstate))
2388 ExplainNode(innerPlanState(planstate), ancestors,
2389 "Inner", NULL, es);
2390
2391 /* special child plans */
2392 switch (nodeTag(plan))
2393 {
2394 case T_Append:
2395 ExplainMemberNodes(((AppendState *) planstate)->appendplans,
2396 ((AppendState *) planstate)->as_nplans,
2397 ancestors, es);
2398 break;
2399 case T_MergeAppend:
2400 ExplainMemberNodes(((MergeAppendState *) planstate)->mergeplans,
2401 ((MergeAppendState *) planstate)->ms_nplans,
2402 ancestors, es);
2403 break;
2404 case T_BitmapAnd:
2405 ExplainMemberNodes(((BitmapAndState *) planstate)->bitmapplans,
2406 ((BitmapAndState *) planstate)->nplans,
2407 ancestors, es);
2408 break;
2409 case T_BitmapOr:
2410 ExplainMemberNodes(((BitmapOrState *) planstate)->bitmapplans,
2411 ((BitmapOrState *) planstate)->nplans,
2412 ancestors, es);
2413 break;
2414 case T_SubqueryScan:
2415 ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
2416 "Subquery", NULL, es);
2417 break;
2418 case T_CustomScan:
2420 ancestors, es);
2421 break;
2422 default:
2423 break;
2424 }
2425
2426 /* subPlan-s */
2427 if (planstate->subPlan)
2428 ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
2429
2430 /* end of child plans */
2431 if (haschildren)
2432 {
2433 ancestors = list_delete_first(ancestors);
2434 ExplainCloseGroup("Plans", "Plans", false, es);
2435 }
2436
2437 /* in text format, undo whatever indentation we added */
2438 if (es->format == EXPLAIN_FORMAT_TEXT)
2439 es->indent = save_indent;
2440
2441 ExplainCloseGroup("Plan",
2442 relationship ? NULL : "Plan",
2443 true, es);
2444}
#define outerPlanState(node)
Definition: execnodes.h:1249
#define innerPlanState(node)
Definition: execnodes.h:1248
static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors, ExplainState *es)
Definition: explain.c:4532
static void show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2450
static void show_memoize_info(MemoizeState *mstate, List *ancestors, ExplainState *es)
Definition: explain.c:3594
static void show_group_keys(GroupState *gstate, List *ancestors, ExplainState *es)
Definition: explain.c:2760
static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
Definition: explain.c:4385
static void show_window_def(WindowAggState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2900
static void show_agg_keys(AggState *astate, List *ancestors, ExplainState *es)
Definition: explain.c:2628
static void show_hashagg_info(AggState *aggstate, ExplainState *es)
Definition: explain.c:3728
static void show_scan_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2552
static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir, ExplainState *es)
Definition: explain.c:4336
static void show_instrumentation_count(const char *qlabel, int which, PlanState *planstate, ExplainState *es)
Definition: explain.c:3977
static void ExplainMemberNodes(PlanState **planstates, int nplans, List *ancestors, ExplainState *es)
Definition: explain.c:4754
static void show_ctescan_info(CteScanState *ctescanstate, ExplainState *es)
Definition: explain.c:3525
static void ExplainPrintJIT(ExplainState *es, int jit_flags, JitInstrumentation *ji)
Definition: explain.c:917
static void show_recursive_union_info(RecursiveUnionState *rstate, ExplainState *es)
Definition: explain.c:3563
static void show_incremental_sort_info(IncrementalSortState *incrsortstate, ExplainState *es)
Definition: explain.c:3301
static void show_tablesample(TableSampleClause *tsc, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:3030
static void show_upper_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2566
static void show_sort_info(SortState *sortstate, ExplainState *es)
Definition: explain.c:3096
static void ExplainMissingMembers(int nplans, int nchildren, ExplainState *es)
Definition: explain.c:4772
explain_per_node_hook_type explain_per_node_hook
Definition: explain.c:57
static void ExplainFlushWorkersState(ExplainState *es)
Definition: explain.c:4966
static void show_hash_info(HashState *hashstate, ExplainState *es)
Definition: explain.c:3387
static void show_table_func_scan_info(TableFuncScanState *tscanstate, ExplainState *es)
Definition: explain.c:3544
static void show_indexsearches_info(PlanState *planstate, ExplainState *es)
Definition: explain.c:3850
static void show_expression(Node *node, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
Definition: explain.c:2508
static bool plan_is_disabled(Plan *plan)
Definition: explain.c:1258
static void show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es)
Definition: explain.c:3908
static void show_windowagg_info(WindowAggState *winstate, ExplainState *es)
Definition: explain.c:3502
static void ExplainSubPlans(List *plans, List *ancestors, const char *relationship, ExplainState *es)
Definition: explain.c:4786
static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
Definition: explain.c:4006
static void ExplainScanTarget(Scan *plan, ExplainState *es)
Definition: explain.c:4372
static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors, ExplainState *es)
Definition: explain.c:2612
static void ExplainCloseWorker(int n, ExplainState *es)
Definition: explain.c:4930
static void ExplainOpenWorker(int n, ExplainState *es)
Definition: explain.c:4868
static void show_wal_usage(ExplainState *es, const WalUsage *usage)
Definition: explain.c:4267
static void show_material_info(MaterialState *mstate, ExplainState *es)
Definition: explain.c:3479
static void show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
Definition: explain.c:2580
static ExplainWorkersState * ExplainCreateWorkersState(int num_workers)
Definition: explain.c:4851
static void show_buffer_usage(ExplainState *es, const BufferUsage *usage)
Definition: explain.c:4098
static void show_incremental_sort_keys(IncrementalSortState *incrsortstate, List *ancestors, ExplainState *es)
Definition: explain.c:2595
static void ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es)
Definition: explain.c:4829
void ExplainIndentText(ExplainState *es)
void ExplainPropertyFloat(const char *qlabel, const char *unit, double value, int ndigits, ExplainState *es)
void ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:140
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:78
List * lappend(List *list, void *datum)
Definition: list.c:339
List * list_delete_first(List *list)
Definition: list.c:943
List * lcons(void *datum, List *list)
Definition: list.c:495
Expr * make_orclause(List *orclauses)
Definition: makefuncs.c:743
Expr * make_andclause(List *andclauses)
Definition: makefuncs.c:727
@ SETOPCMD_EXCEPT
Definition: nodes.h:402
@ SETOPCMD_EXCEPT_ALL
Definition: nodes.h:403
@ SETOPCMD_INTERSECT_ALL
Definition: nodes.h:401
@ SETOPCMD_INTERSECT
Definition: nodes.h:400
@ SETOP_HASHED
Definition: nodes.h:409
@ SETOP_SORTED
Definition: nodes.h:408
#define DO_AGGSPLIT_SKIPFINAL(as)
Definition: nodes.h:388
#define IsA(nodeptr, _type_)
Definition: nodes.h:160
#define nodeTag(nodeptr)
Definition: nodes.h:135
#define DO_AGGSPLIT_COMBINE(as)
Definition: nodes.h:387
@ CMD_MERGE
Definition: nodes.h:271
@ CMD_INSERT
Definition: nodes.h:269
@ CMD_DELETE
Definition: nodes.h:270
@ CMD_UPDATE
Definition: nodes.h:268
@ CMD_SELECT
Definition: nodes.h:267
@ AGG_SORTED
Definition: nodes.h:357
@ AGG_HASHED
Definition: nodes.h:358
@ AGG_MIXED
Definition: nodes.h:359
@ AGG_PLAIN
Definition: nodes.h:356
#define castNode(_type_, nodeptr)
Definition: nodes.h:178
@ JOIN_SEMI
Definition: nodes.h:309
@ JOIN_FULL
Definition: nodes.h:297
@ JOIN_INNER
Definition: nodes.h:295
@ JOIN_RIGHT
Definition: nodes.h:298
@ JOIN_RIGHT_SEMI
Definition: nodes.h:311
@ JOIN_LEFT
Definition: nodes.h:296
@ JOIN_RIGHT_ANTI
Definition: nodes.h:312
@ JOIN_ANTI
Definition: nodes.h:310
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
static const struct fns functions
Definition: regcomp.c:358
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242
AggSplit aggsplit
Definition: plannodes.h:1143
AggStrategy aggstrategy
Definition: plannodes.h:1140
void(* ExplainCustomScan)(CustomScanState *node, List *ancestors, ExplainState *es)
Definition: extensible.h:155
const struct CustomExecMethods * methods
Definition: execnodes.h:2130
int es_jit_flags
Definition: execnodes.h:756
int num_workers
Definition: plannodes.h:1310
int num_workers
Definition: plannodes.h:1286
bool single_copy
Definition: plannodes.h:1290
ScanDirection indexorderdir
Definition: plannodes.h:604
ScanDirection indexorderdir
Definition: plannodes.h:556
Oid indexid
Definition: plannodes.h:544
double startup
Definition: instrument.h:85
WalUsage walusage
Definition: instrument.h:93
double ntuples
Definition: instrument.h:87
BufferUsage bufusage
Definition: instrument.h:92
double ntuples2
Definition: instrument.h:88
Definition: pg_list.h:54
Definition: nodes.h:131
struct SharedJitInstrumentation * worker_jit_instrument
Definition: execnodes.h:1167
Instrumentation * instrument
Definition: execnodes.h:1163
Plan * plan
Definition: execnodes.h:1153
EState * state
Definition: execnodes.h:1155
WorkerInstrumentation * worker_instrument
Definition: execnodes.h:1164
JitInstrumentation jit_instr[FLEXIBLE_ARRAY_MEMBER]
Definition: jit.h:54
Instrumentation instrument[FLEXIBLE_ARRAY_MEMBER]
Definition: instrument.h:99

References AGG_HASHED, AGG_MIXED, AGG_PLAIN, AGG_SORTED, Agg::aggsplit, Agg::aggstrategy, ExplainState::analyze, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), ExplainState::buffers, Instrumentation::bufusage, castNode, CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_SELECT, CMD_UPDATE, ExplainState::costs, DO_AGGSPLIT_COMBINE, DO_AGGSPLIT_SKIPFINAL, EState::es_jit_flags, EXPLAIN_FORMAT_TEXT, explain_get_index_name(), explain_per_node_hook, ExplainCloseGroup(), ExplainCloseWorker(), ExplainCreateWorkersState(), ExplainCustomChildren(), CustomExecMethods::ExplainCustomScan, ExplainFlushWorkersState(), ExplainIndentText(), ExplainIndexScanDetails(), ExplainMemberNodes(), ExplainMissingMembers(), ExplainModifyTarget(), ExplainNode(), ExplainOpenGroup(), ExplainOpenWorker(), ExplainPrintJIT(), ExplainPropertyBool(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyText(), ExplainScanTarget(), ExplainSubPlans(), ExplainState::format, RangeTblFunction::funcexpr, functions, ExplainState::hide_workers, if(), ExplainState::indent, IndexScan::indexid, IndexOnlyScan::indexid, BitmapIndexScan::indexid, IndexScan::indexorderdir, IndexOnlyScan::indexorderdir, PlanState::initPlan, innerPlanState, InstrEndLoop(), WorkerInstrumentation::instrument, PlanState::instrument, IsA, SharedJitInstrumentation::jit_instr, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_RIGHT_ANTI, JOIN_RIGHT_SEMI, JOIN_SEMI, lappend(), lcons(), lfirst, list_delete_first(), list_length(), list_make1, make_andclause(), make_orclause(), CustomScanState::methods, NIL, Instrumentation::nloops, nodeTag, Instrumentation::ntuples, Instrumentation::ntuples2, WorkerInstrumentation::num_workers, SharedJitInstrumentation::num_workers, Gather::num_workers, GatherMerge::num_workers, outerPlanState, PlanState::plan, plan, plan_is_disabled(), psprintf(), quote_identifier(), SETOP_HASHED, SETOP_SORTED, SETOPCMD_EXCEPT, SETOPCMD_EXCEPT_ALL, SETOPCMD_INTERSECT, SETOPCMD_INTERSECT_ALL, show_agg_keys(), show_buffer_usage(), show_ctescan_info(), show_expression(), show_foreignscan_info(), show_group_keys(), show_hash_info(), show_hashagg_info(), show_incremental_sort_info(), show_incremental_sort_keys(), show_indexsearches_info(), show_instrumentation_count(), show_material_info(), show_memoize_info(), show_merge_append_keys(), show_modifytable_info(), show_plan_tlist(), show_recursive_union_info(), show_scan_qual(), show_sort_info(), show_sort_keys(), show_table_func_scan_info(), show_tablesample(), show_tidbitmap_info(), show_upper_qual(), show_wal_usage(), show_window_def(), show_windowagg_info(), Gather::single_copy, Instrumentation::startup, PlanState::state, ExplainState::str, PlanState::subPlan, ExplainState::timing, Instrumentation::total, ExplainState::verbose, ExplainState::wal, Instrumentation::walusage, PlanState::worker_instrument, PlanState::worker_jit_instrument, and ExplainState::workers_state.

Referenced by ExplainCustomChildren(), ExplainMemberNodes(), ExplainNode(), ExplainPrintPlan(), and ExplainSubPlans().

◆ ExplainOnePlan()

void ExplainOnePlan ( PlannedStmt plannedstmt,
CachedPlan cplan,
CachedPlanSource plansource,
int  query_index,
IntoClause into,
ExplainState es,
const char *  queryString,
ParamListInfo  params,
QueryEnvironment queryEnv,
const instr_time planduration,
const BufferUsage bufusage,
const MemoryContextCounters mem_counters 
)

Definition at line 495 of file explain.c.

502{
504 QueryDesc *queryDesc;
505 instr_time starttime;
506 double totaltime = 0;
507 int eflags;
508 int instrument_option = 0;
509 SerializeMetrics serializeMetrics = {0};
510
511 Assert(plannedstmt->commandType != CMD_UTILITY);
512
513 if (es->analyze && es->timing)
514 instrument_option |= INSTRUMENT_TIMER;
515 else if (es->analyze)
516 instrument_option |= INSTRUMENT_ROWS;
517
518 if (es->buffers)
519 instrument_option |= INSTRUMENT_BUFFERS;
520 if (es->wal)
521 instrument_option |= INSTRUMENT_WAL;
522
523 /*
524 * We always collect timing for the entire statement, even when node-level
525 * timing is off, so we don't look at es->timing here. (We could skip
526 * this if !es->summary, but it's hardly worth the complication.)
527 */
528 INSTR_TIME_SET_CURRENT(starttime);
529
530 /*
531 * Use a snapshot with an updated command ID to ensure this query sees
532 * results of any previously executed queries.
533 */
536
537 /*
538 * We discard the output if we have no use for it. If we're explaining
539 * CREATE TABLE AS, we'd better use the appropriate tuple receiver, while
540 * the SERIALIZE option requires its own tuple receiver. (If you specify
541 * SERIALIZE while explaining CREATE TABLE AS, you'll see zeroes for the
542 * results, which is appropriate since no data would have gone to the
543 * client.)
544 */
545 if (into)
547 else if (es->serialize != EXPLAIN_SERIALIZE_NONE)
549 else
551
552 /* Create a QueryDesc for the query */
553 queryDesc = CreateQueryDesc(plannedstmt, cplan, queryString,
555 dest, params, queryEnv, instrument_option);
556
557 /* Select execution options */
558 if (es->analyze)
559 eflags = 0; /* default run-to-completion flags */
560 else
561 eflags = EXEC_FLAG_EXPLAIN_ONLY;
562 if (es->generic)
564 if (into)
565 eflags |= GetIntoRelEFlags(into);
566
567 /* Prepare the plan for execution. */
568 if (queryDesc->cplan)
569 {
570 ExecutorStartCachedPlan(queryDesc, eflags, plansource, query_index);
571 Assert(queryDesc->planstate);
572 }
573 else
574 {
575 if (!ExecutorStart(queryDesc, eflags))
576 elog(ERROR, "ExecutorStart() failed unexpectedly");
577 }
578
579 /* Execute the plan for statistics if asked for */
580 if (es->analyze)
581 {
582 ScanDirection dir;
583
584 /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
585 if (into && into->skipData)
587 else
589
590 /* run the plan */
591 ExecutorRun(queryDesc, dir, 0);
592
593 /* run cleanup too */
594 ExecutorFinish(queryDesc);
595
596 /* We can't run ExecutorEnd 'till we're done printing the stats... */
597 totaltime += elapsed_time(&starttime);
598 }
599
600 /* grab serialization metrics before we destroy the DestReceiver */
602 serializeMetrics = GetSerializationMetrics(dest);
603
604 /* call the DestReceiver's destroy method even during explain */
605 dest->rDestroy(dest);
606
607 ExplainOpenGroup("Query", NULL, true, es);
608
609 /* Create textual dump of plan tree */
610 ExplainPrintPlan(es, queryDesc);
611
612 /* Show buffer and/or memory usage in planning */
613 if (peek_buffer_usage(es, bufusage) || mem_counters)
614 {
615 ExplainOpenGroup("Planning", "Planning", true, es);
616
617 if (es->format == EXPLAIN_FORMAT_TEXT)
618 {
620 appendStringInfoString(es->str, "Planning:\n");
621 es->indent++;
622 }
623
624 if (bufusage)
625 show_buffer_usage(es, bufusage);
626
627 if (mem_counters)
628 show_memory_counters(es, mem_counters);
629
630 if (es->format == EXPLAIN_FORMAT_TEXT)
631 es->indent--;
632
633 ExplainCloseGroup("Planning", "Planning", true, es);
634 }
635
636 if (es->summary && planduration)
637 {
638 double plantime = INSTR_TIME_GET_DOUBLE(*planduration);
639
640 ExplainPropertyFloat("Planning Time", "ms", 1000.0 * plantime, 3, es);
641 }
642
643 /* Print info about runtime of triggers */
644 if (es->analyze)
645 ExplainPrintTriggers(es, queryDesc);
646
647 /*
648 * Print info about JITing. Tied to es->costs because we don't want to
649 * display this in regression tests, as it'd cause output differences
650 * depending on build options. Might want to separate that out from COSTS
651 * at a later stage.
652 */
653 if (es->costs)
654 ExplainPrintJITSummary(es, queryDesc);
655
656 /* Print info about serialization of output */
658 ExplainPrintSerialize(es, &serializeMetrics);
659
660 /* Allow plugins to print additional information */
662 (*explain_per_plan_hook) (plannedstmt, into, es, queryString,
663 params, queryEnv);
664
665 /*
666 * Close down the query and free resources. Include time for this in the
667 * total execution time (although it should be pretty minimal).
668 */
669 INSTR_TIME_SET_CURRENT(starttime);
670
671 ExecutorEnd(queryDesc);
672
673 FreeQueryDesc(queryDesc);
674
676
677 /* We need a CCI just in case query expanded to multiple plans */
678 if (es->analyze)
680
681 totaltime += elapsed_time(&starttime);
682
683 /*
684 * We only report execution time if we actually ran the query (that is,
685 * the user specified ANALYZE), and if summary reporting is enabled (the
686 * user can set SUMMARY OFF to not have the timing information included in
687 * the output). By default, ANALYZE sets SUMMARY to true.
688 */
689 if (es->summary && es->analyze)
690 ExplainPropertyFloat("Execution Time", "ms", 1000.0 * totaltime, 3,
691 es);
692
693 ExplainCloseGroup("Query", NULL, true, es);
694}
int GetIntoRelEFlags(IntoClause *intoClause)
Definition: createas.c:376
DestReceiver * CreateIntoRelDestReceiver(IntoClause *intoClause)
Definition: createas.c:441
DestReceiver * None_Receiver
Definition: dest.c:96
bool ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:125
void ExecutorStartCachedPlan(QueryDesc *queryDesc, int eflags, CachedPlanSource *plansource, int query_index)
Definition: execMain.c:292
void ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:535
void ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:472
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
Definition: execMain.c:362
#define EXEC_FLAG_EXPLAIN_GENERIC
Definition: executor.h:67
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:66
static bool peek_buffer_usage(ExplainState *es, const BufferUsage *usage)
Definition: explain.c:4058
void ExplainPrintJITSummary(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:891
static double elapsed_time(instr_time *starttime)
Definition: explain.c:1179
explain_per_plan_hook_type explain_per_plan_hook
Definition: explain.c:56
static void show_memory_counters(ExplainState *es, const MemoryContextCounters *mem_counters)
Definition: explain.c:4310
void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:771
static void ExplainPrintSerialize(ExplainState *es, SerializeMetrics *metrics)
Definition: explain.c:1015
void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:848
SerializeMetrics GetSerializationMetrics(DestReceiver *dest)
Definition: explain_dr.c:299
DestReceiver * CreateExplainSerializeDestReceiver(ExplainState *es)
Definition: explain_dr.c:274
@ EXPLAIN_SERIALIZE_NONE
Definition: explain_state.h:22
@ INSTRUMENT_TIMER
Definition: instrument.h:62
@ INSTRUMENT_BUFFERS
Definition: instrument.h:63
@ INSTRUMENT_WAL
Definition: instrument.h:65
@ INSTRUMENT_ROWS
Definition: instrument.h:64
@ CMD_UTILITY
Definition: nodes.h:272
void FreeQueryDesc(QueryDesc *qdesc)
Definition: pquery.c:112
QueryDesc * CreateQueryDesc(PlannedStmt *plannedstmt, CachedPlan *cplan, const char *sourceText, Snapshot snapshot, Snapshot crosscheck_snapshot, DestReceiver *dest, ParamListInfo params, QueryEnvironment *queryEnv, int instrument_options)
Definition: pquery.c:72
ScanDirection
Definition: sdir.h:25
@ NoMovementScanDirection
Definition: sdir.h:27
void UpdateActiveSnapshotCommandId(void)
Definition: snapmgr.c:731
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
void PushCopiedSnapshot(Snapshot snapshot)
Definition: snapmgr.c:719
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:787
#define InvalidSnapshot
Definition: snapshot.h:119
ExplainSerializeOption serialize
Definition: explain_state.h:58
bool skipData
Definition: primnodes.h:171
CmdType commandType
Definition: plannodes.h:53
CachedPlan * cplan
Definition: execdesc.h:38
PlanState * planstate
Definition: execdesc.h:50
void CommandCounterIncrement(void)
Definition: xact.c:1100

References ExplainState::analyze, appendStringInfoString(), Assert(), ExplainState::buffers, CMD_UTILITY, CommandCounterIncrement(), PlannedStmt::commandType, ExplainState::costs, QueryDesc::cplan, CreateExplainSerializeDestReceiver(), CreateIntoRelDestReceiver(), CreateQueryDesc(), generate_unaccent_rules::dest, elapsed_time(), elog, ERROR, EXEC_FLAG_EXPLAIN_GENERIC, EXEC_FLAG_EXPLAIN_ONLY, ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), ExecutorStartCachedPlan(), EXPLAIN_FORMAT_TEXT, explain_per_plan_hook, EXPLAIN_SERIALIZE_NONE, ExplainCloseGroup(), ExplainIndentText(), ExplainOpenGroup(), ExplainPrintJITSummary(), ExplainPrintPlan(), ExplainPrintSerialize(), ExplainPrintTriggers(), ExplainPropertyFloat(), ExplainState::format, ForwardScanDirection, FreeQueryDesc(), ExplainState::generic, GetActiveSnapshot(), GetIntoRelEFlags(), GetSerializationMetrics(), ExplainState::indent, INSTR_TIME_GET_DOUBLE, INSTR_TIME_SET_CURRENT, INSTRUMENT_BUFFERS, INSTRUMENT_ROWS, INSTRUMENT_TIMER, INSTRUMENT_WAL, InvalidSnapshot, NoMovementScanDirection, None_Receiver, peek_buffer_usage(), QueryDesc::planstate, PopActiveSnapshot(), PushCopiedSnapshot(), ExplainState::serialize, show_buffer_usage(), show_memory_counters(), IntoClause::skipData, ExplainState::str, ExplainState::summary, ExplainState::timing, UpdateActiveSnapshotCommandId(), and ExplainState::wal.

Referenced by ExplainExecuteQuery(), and standard_ExplainOneQuery().

◆ ExplainOneQuery()

static void ExplainOneQuery ( Query query,
int  cursorOptions,
IntoClause into,
ExplainState es,
ParseState pstate,
ParamListInfo  params 
)
static

Definition at line 293 of file explain.c.

296{
297 /* planner will not cope with utility statements */
298 if (query->commandType == CMD_UTILITY)
299 {
300 ExplainOneUtility(query->utilityStmt, into, es, pstate, params);
301 return;
302 }
303
304 /* if an advisor plugin is present, let it manage things */
306 (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
307 pstate->p_sourcetext, params, pstate->p_queryEnv);
308 else
309 standard_ExplainOneQuery(query, cursorOptions, into, es,
310 pstate->p_sourcetext, params, pstate->p_queryEnv);
311}
ExplainOneQuery_hook_type ExplainOneQuery_hook
Definition: explain.c:50
void ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
Definition: explain.c:391
void standard_ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: explain.c:318
QueryEnvironment * p_queryEnv
Definition: parse_node.h:239
const char * p_sourcetext
Definition: parse_node.h:209
CmdType commandType
Definition: parsenodes.h:121
Node * utilityStmt
Definition: parsenodes.h:136

References CMD_UTILITY, Query::commandType, ExplainOneQuery_hook, ExplainOneUtility(), ParseState::p_queryEnv, ParseState::p_sourcetext, standard_ExplainOneQuery(), and Query::utilityStmt.

Referenced by ExplainOneUtility(), and ExplainQuery().

◆ ExplainOneUtility()

void ExplainOneUtility ( Node utilityStmt,
IntoClause into,
ExplainState es,
ParseState pstate,
ParamListInfo  params 
)

Definition at line 391 of file explain.c.

393{
394 if (utilityStmt == NULL)
395 return;
396
397 if (IsA(utilityStmt, CreateTableAsStmt))
398 {
399 /*
400 * We have to rewrite the contained SELECT and then pass it back to
401 * ExplainOneQuery. Copy to be safe in the EXPLAIN EXECUTE case.
402 */
403 CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
404 Query *ctas_query;
405 List *rewritten;
406 JumbleState *jstate = NULL;
407
408 /*
409 * Check if the relation exists or not. This is done at this stage to
410 * avoid query planning or execution.
411 */
412 if (CreateTableAsRelExists(ctas))
413 {
414 if (ctas->objtype == OBJECT_TABLE)
415 ExplainDummyGroup("CREATE TABLE AS", NULL, es);
416 else if (ctas->objtype == OBJECT_MATVIEW)
417 ExplainDummyGroup("CREATE MATERIALIZED VIEW", NULL, es);
418 else
419 elog(ERROR, "unexpected object type: %d",
420 (int) ctas->objtype);
421 return;
422 }
423
424 ctas_query = castNode(Query, copyObject(ctas->query));
425 if (IsQueryIdEnabled())
426 jstate = JumbleQuery(ctas_query);
428 (*post_parse_analyze_hook) (pstate, ctas_query, jstate);
429 rewritten = QueryRewrite(ctas_query);
430 Assert(list_length(rewritten) == 1);
432 CURSOR_OPT_PARALLEL_OK, ctas->into, es,
433 pstate, params);
434 }
435 else if (IsA(utilityStmt, DeclareCursorStmt))
436 {
437 /*
438 * Likewise for DECLARE CURSOR.
439 *
440 * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
441 * actually run the query. This is different from pre-8.3 behavior
442 * but seems more useful than not running the query. No cursor will
443 * be created, however.
444 */
445 DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
446 Query *dcs_query;
447 List *rewritten;
448 JumbleState *jstate = NULL;
449
450 dcs_query = castNode(Query, copyObject(dcs->query));
451 if (IsQueryIdEnabled())
452 jstate = JumbleQuery(dcs_query);
454 (*post_parse_analyze_hook) (pstate, dcs_query, jstate);
455
456 rewritten = QueryRewrite(dcs_query);
457 Assert(list_length(rewritten) == 1);
459 dcs->options, NULL, es,
460 pstate, params);
461 }
462 else if (IsA(utilityStmt, ExecuteStmt))
463 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
464 pstate, params);
465 else if (IsA(utilityStmt, NotifyStmt))
466 {
467 if (es->format == EXPLAIN_FORMAT_TEXT)
468 appendStringInfoString(es->str, "NOTIFY\n");
469 else
470 ExplainDummyGroup("Notify", NULL, es);
471 }
472 else
473 {
474 if (es->format == EXPLAIN_FORMAT_TEXT)
476 "Utility statements have no plan structure\n");
477 else
478 ExplainDummyGroup("Utility Statement", NULL, es);
479 }
480}
void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
Definition: prepare.c:572
bool CreateTableAsRelExists(CreateTableAsStmt *ctas)
Definition: createas.c:394
static void ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
Definition: explain.c:293
void ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
#define copyObject(obj)
Definition: nodes.h:226
@ OBJECT_MATVIEW
Definition: parsenodes.h:2335
@ OBJECT_TABLE
Definition: parsenodes.h:2353
#define CURSOR_OPT_PARALLEL_OK
Definition: parsenodes.h:3378
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:59
#define linitial_node(type, l)
Definition: pg_list.h:181
static bool IsQueryIdEnabled(void)
Definition: queryjumble.h:84
JumbleState * JumbleQuery(Query *query)
List * QueryRewrite(Query *parsetree)
IntoClause * into
Definition: parsenodes.h:3981
ObjectType objtype
Definition: parsenodes.h:3982

References appendStringInfoString(), Assert(), castNode, copyObject, CreateTableAsRelExists(), CURSOR_OPT_PARALLEL_OK, elog, ERROR, EXPLAIN_FORMAT_TEXT, ExplainDummyGroup(), ExplainExecuteQuery(), ExplainOneQuery(), ExplainState::format, CreateTableAsStmt::into, IsA, IsQueryIdEnabled(), JumbleQuery(), linitial_node, list_length(), OBJECT_MATVIEW, OBJECT_TABLE, CreateTableAsStmt::objtype, DeclareCursorStmt::options, post_parse_analyze_hook, DeclareCursorStmt::query, CreateTableAsStmt::query, QueryRewrite(), and ExplainState::str.

Referenced by ExplainExecuteQuery(), and ExplainOneQuery().

◆ ExplainOpenWorker()

static void ExplainOpenWorker ( int  n,
ExplainState es 
)
static

Definition at line 4868 of file explain.c.

4869{
4870 ExplainWorkersState *wstate = es->workers_state;
4871
4872 Assert(wstate);
4873 Assert(n >= 0 && n < wstate->num_workers);
4874
4875 /* Save prior output buffer pointer */
4876 wstate->prev_str = es->str;
4877
4878 if (!wstate->worker_inited[n])
4879 {
4880 /* First time through, so create the buffer for this worker */
4881 initStringInfo(&wstate->worker_str[n]);
4882 es->str = &wstate->worker_str[n];
4883
4884 /*
4885 * Push suitable initial formatting state for this worker's field
4886 * group. We allow one extra logical nesting level, since this group
4887 * will eventually be wrapped in an outer "Workers" group.
4888 */
4889 ExplainOpenSetAsideGroup("Worker", NULL, true, 2, es);
4890
4891 /*
4892 * In non-TEXT formats we always emit a "Worker Number" field, even if
4893 * there's no other data for this worker.
4894 */
4895 if (es->format != EXPLAIN_FORMAT_TEXT)
4896 ExplainPropertyInteger("Worker Number", NULL, n, es);
4897
4898 wstate->worker_inited[n] = true;
4899 }
4900 else
4901 {
4902 /* Resuming output for a worker we've already emitted some data for */
4903 es->str = &wstate->worker_str[n];
4904
4905 /* Restore formatting state saved by last ExplainCloseWorker() */
4906 ExplainRestoreGroup(es, 2, &wstate->worker_state_save[n]);
4907 }
4908
4909 /*
4910 * In TEXT format, prefix the first output line for this worker with
4911 * "Worker N:". Then, any additional lines should be indented one more
4912 * stop than the "Worker N" line is.
4913 */
4914 if (es->format == EXPLAIN_FORMAT_TEXT)
4915 {
4916 if (es->str->len == 0)
4917 {
4919 appendStringInfo(es->str, "Worker %d: ", n);
4920 }
4921
4922 es->indent++;
4923 }
4924}
void ExplainOpenSetAsideGroup(const char *objtype, const char *labelname, bool labeled, int depth, ExplainState *es)
void ExplainRestoreGroup(ExplainState *es, int depth, int *state_save)
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97

References appendStringInfo(), Assert(), EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainOpenSetAsideGroup(), ExplainPropertyInteger(), ExplainRestoreGroup(), ExplainState::format, ExplainState::indent, initStringInfo(), StringInfoData::len, ExplainWorkersState::prev_str, ExplainState::str, ExplainWorkersState::worker_inited, ExplainWorkersState::worker_state_save, ExplainWorkersState::worker_str, and ExplainState::workers_state.

Referenced by ExplainNode(), show_hashagg_info(), show_incremental_sort_info(), show_memoize_info(), show_sort_info(), and show_tidbitmap_info().

◆ ExplainPreScanNode()

static bool ExplainPreScanNode ( PlanState planstate,
Bitmapset **  rels_used 
)
static

Definition at line 1198 of file explain.c.

1199{
1200 Plan *plan = planstate->plan;
1201
1202 switch (nodeTag(plan))
1203 {
1204 case T_SeqScan:
1205 case T_SampleScan:
1206 case T_IndexScan:
1207 case T_IndexOnlyScan:
1208 case T_BitmapHeapScan:
1209 case T_TidScan:
1210 case T_TidRangeScan:
1211 case T_SubqueryScan:
1212 case T_FunctionScan:
1213 case T_TableFuncScan:
1214 case T_ValuesScan:
1215 case T_CteScan:
1216 case T_NamedTuplestoreScan:
1217 case T_WorkTableScan:
1218 *rels_used = bms_add_member(*rels_used,
1219 ((Scan *) plan)->scanrelid);
1220 break;
1221 case T_ForeignScan:
1222 *rels_used = bms_add_members(*rels_used,
1223 ((ForeignScan *) plan)->fs_base_relids);
1224 break;
1225 case T_CustomScan:
1226 *rels_used = bms_add_members(*rels_used,
1227 ((CustomScan *) plan)->custom_relids);
1228 break;
1229 case T_ModifyTable:
1230 *rels_used = bms_add_member(*rels_used,
1231 ((ModifyTable *) plan)->nominalRelation);
1232 if (((ModifyTable *) plan)->exclRelRTI)
1233 *rels_used = bms_add_member(*rels_used,
1234 ((ModifyTable *) plan)->exclRelRTI);
1235 break;
1236 case T_Append:
1237 *rels_used = bms_add_members(*rels_used,
1238 ((Append *) plan)->apprelids);
1239 break;
1240 case T_MergeAppend:
1241 *rels_used = bms_add_members(*rels_used,
1242 ((MergeAppend *) plan)->apprelids);
1243 break;
1244 default:
1245 break;
1246 }
1247
1248 return planstate_tree_walker(planstate, ExplainPreScanNode, rels_used);
1249}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
Definition: explain.c:1198
#define planstate_tree_walker(ps, w, c)
Definition: nodeFuncs.h:179

References bms_add_member(), bms_add_members(), ExplainPreScanNode(), nodeTag, PlanState::plan, plan, and planstate_tree_walker.

Referenced by ExplainPreScanNode(), and ExplainPrintPlan().

◆ ExplainPrintJIT()

static void ExplainPrintJIT ( ExplainState es,
int  jit_flags,
JitInstrumentation ji 
)
static

Definition at line 917 of file explain.c.

918{
919 instr_time total_time;
920
921 /* don't print information if no JITing happened */
922 if (!ji || ji->created_functions == 0)
923 return;
924
925 /* calculate total time */
926 INSTR_TIME_SET_ZERO(total_time);
927 /* don't add deform_counter, it's included in generation_counter */
928 INSTR_TIME_ADD(total_time, ji->generation_counter);
929 INSTR_TIME_ADD(total_time, ji->inlining_counter);
930 INSTR_TIME_ADD(total_time, ji->optimization_counter);
931 INSTR_TIME_ADD(total_time, ji->emission_counter);
932
933 ExplainOpenGroup("JIT", "JIT", true, es);
934
935 /* for higher density, open code the text output format */
936 if (es->format == EXPLAIN_FORMAT_TEXT)
937 {
939 appendStringInfoString(es->str, "JIT:\n");
940 es->indent++;
941
942 ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
943
945 appendStringInfo(es->str, "Options: %s %s, %s %s, %s %s, %s %s\n",
946 "Inlining", jit_flags & PGJIT_INLINE ? "true" : "false",
947 "Optimization", jit_flags & PGJIT_OPT3 ? "true" : "false",
948 "Expressions", jit_flags & PGJIT_EXPR ? "true" : "false",
949 "Deforming", jit_flags & PGJIT_DEFORM ? "true" : "false");
950
951 if (es->analyze && es->timing)
952 {
955 "Timing: %s %.3f ms (%s %.3f ms), %s %.3f ms, %s %.3f ms, %s %.3f ms, %s %.3f ms\n",
956 "Generation", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->generation_counter),
957 "Deform", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->deform_counter),
958 "Inlining", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->inlining_counter),
959 "Optimization", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->optimization_counter),
960 "Emission", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->emission_counter),
961 "Total", 1000.0 * INSTR_TIME_GET_DOUBLE(total_time));
962 }
963
964 es->indent--;
965 }
966 else
967 {
968 ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
969
970 ExplainOpenGroup("Options", "Options", true, es);
971 ExplainPropertyBool("Inlining", jit_flags & PGJIT_INLINE, es);
972 ExplainPropertyBool("Optimization", jit_flags & PGJIT_OPT3, es);
973 ExplainPropertyBool("Expressions", jit_flags & PGJIT_EXPR, es);
974 ExplainPropertyBool("Deforming", jit_flags & PGJIT_DEFORM, es);
975 ExplainCloseGroup("Options", "Options", true, es);
976
977 if (es->analyze && es->timing)
978 {
979 ExplainOpenGroup("Timing", "Timing", true, es);
980
981 ExplainOpenGroup("Generation", "Generation", true, es);
982 ExplainPropertyFloat("Deform", "ms",
984 3, es);
985 ExplainPropertyFloat("Total", "ms",
987 3, es);
988 ExplainCloseGroup("Generation", "Generation", true, es);
989
990 ExplainPropertyFloat("Inlining", "ms",
992 3, es);
993 ExplainPropertyFloat("Optimization", "ms",
995 3, es);
996 ExplainPropertyFloat("Emission", "ms",
998 3, es);
999 ExplainPropertyFloat("Total", "ms",
1000 1000.0 * INSTR_TIME_GET_DOUBLE(total_time),
1001 3, es);
1002
1003 ExplainCloseGroup("Timing", "Timing", true, es);
1004 }
1005 }
1006
1007 ExplainCloseGroup("JIT", "JIT", true, es);
1008}
#define INSTR_TIME_ADD(x, y)
Definition: instr_time.h:178
#define INSTR_TIME_SET_ZERO(t)
Definition: instr_time.h:172
#define PGJIT_OPT3
Definition: jit.h:21
#define PGJIT_EXPR
Definition: jit.h:23
#define PGJIT_DEFORM
Definition: jit.h:24
#define PGJIT_INLINE
Definition: jit.h:22
instr_time generation_counter
Definition: jit.h:33
size_t created_functions
Definition: jit.h:30
instr_time optimization_counter
Definition: jit.h:42
instr_time deform_counter
Definition: jit.h:36
instr_time emission_counter
Definition: jit.h:45
instr_time inlining_counter
Definition: jit.h:39

References ExplainState::analyze, appendStringInfo(), appendStringInfoString(), JitInstrumentation::created_functions, JitInstrumentation::deform_counter, JitInstrumentation::emission_counter, EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainIndentText(), ExplainOpenGroup(), ExplainPropertyBool(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainState::format, JitInstrumentation::generation_counter, ExplainState::indent, JitInstrumentation::inlining_counter, INSTR_TIME_ADD, INSTR_TIME_GET_DOUBLE, INSTR_TIME_SET_ZERO, JitInstrumentation::optimization_counter, PGJIT_DEFORM, PGJIT_EXPR, PGJIT_INLINE, PGJIT_OPT3, ExplainState::str, and ExplainState::timing.

Referenced by ExplainNode(), and ExplainPrintJITSummary().

◆ ExplainPrintJITSummary()

void ExplainPrintJITSummary ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 891 of file explain.c.

892{
893 JitInstrumentation ji = {0};
894
895 if (!(queryDesc->estate->es_jit_flags & PGJIT_PERFORM))
896 return;
897
898 /*
899 * Work with a copy instead of modifying the leader state, since this
900 * function may be called twice
901 */
902 if (queryDesc->estate->es_jit)
903 InstrJitAgg(&ji, &queryDesc->estate->es_jit->instr);
904
905 /* If this process has done JIT in parallel workers, merge stats */
906 if (queryDesc->estate->es_jit_worker_instr)
907 InstrJitAgg(&ji, queryDesc->estate->es_jit_worker_instr);
908
909 ExplainPrintJIT(es, queryDesc->estate->es_jit_flags, &ji);
910}
void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
Definition: jit.c:182
#define PGJIT_PERFORM
Definition: jit.h:20
struct JitContext * es_jit
Definition: execnodes.h:757
struct JitInstrumentation * es_jit_worker_instr
Definition: execnodes.h:758
JitInstrumentation instr
Definition: jit.h:62
EState * estate
Definition: execdesc.h:49

References EState::es_jit, EState::es_jit_flags, EState::es_jit_worker_instr, QueryDesc::estate, ExplainPrintJIT(), JitContext::instr, InstrJitAgg(), and PGJIT_PERFORM.

Referenced by explain_ExecutorEnd(), and ExplainOnePlan().

◆ ExplainPrintPlan()

void ExplainPrintPlan ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 771 of file explain.c.

772{
773 Bitmapset *rels_used = NULL;
774 PlanState *ps;
775 ListCell *lc;
776
777 /* Set up ExplainState fields associated with this plan tree */
778 Assert(queryDesc->plannedstmt != NULL);
779 es->pstmt = queryDesc->plannedstmt;
780 es->rtable = queryDesc->plannedstmt->rtable;
781 ExplainPreScanNode(queryDesc->planstate, &rels_used);
784 es->rtable_names);
785 es->printed_subplans = NULL;
786 es->rtable_size = list_length(es->rtable);
787 foreach(lc, es->rtable)
788 {
790
791 if (rte->rtekind == RTE_GROUP)
792 {
793 es->rtable_size--;
794 break;
795 }
796 }
797
798 /*
799 * Sometimes we mark a Gather node as "invisible", which means that it's
800 * not to be displayed in EXPLAIN output. The purpose of this is to allow
801 * running regression tests with debug_parallel_query=regress to get the
802 * same results as running the same tests with debug_parallel_query=off.
803 * Such marking is currently only supported on a Gather at the top of the
804 * plan. We skip that node, and we must also hide per-worker detail data
805 * further down in the plan tree.
806 */
807 ps = queryDesc->planstate;
808 if (IsA(ps, GatherState) && ((Gather *) ps->plan)->invisible)
809 {
811 es->hide_workers = true;
812 }
813 ExplainNode(ps, NIL, NULL, NULL, es);
814
815 /*
816 * If requested, include information about GUC parameters with values that
817 * don't match the built-in defaults.
818 */
820
821 /*
822 * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_AUTO, but we don't show
823 * the queryid in any of the EXPLAIN plans to keep stable the results
824 * generated by regression test suites.
825 */
826 if (es->verbose && queryDesc->plannedstmt->queryId != UINT64CONST(0) &&
828 {
829 /*
830 * Output the queryid as an int64 rather than a uint64 so we match
831 * what would be seen in the BIGINT pg_stat_statements.queryid column.
832 */
833 ExplainPropertyInteger("Query Identifier", NULL, (int64)
834 queryDesc->plannedstmt->queryId, es);
835 }
836}
int64_t int64
Definition: c.h:499
#define UINT64CONST(x)
Definition: c.h:517
static void ExplainPrintSettings(ExplainState *es)
Definition: explain.c:701
struct parser_state ps
@ RTE_GROUP
Definition: parsenodes.h:1037
#define lfirst_node(type, lc)
Definition: pg_list.h:176
@ COMPUTE_QUERY_ID_REGRESS
Definition: queryjumble.h:65
int compute_query_id
List * deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
Definition: ruleutils.c:3753
List * select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
Definition: ruleutils.c:3855
Bitmapset * printed_subplans
Definition: explain_state.h:68
List * rtable_names
Definition: explain_state.h:66
PlannedStmt * pstmt
Definition: explain_state.h:64
List * deparse_cxt
Definition: explain_state.h:67
List * rtable
Definition: plannodes.h:88
uint64 queryId
Definition: plannodes.h:56
PlannedStmt * plannedstmt
Definition: execdesc.h:37
RTEKind rtekind
Definition: parsenodes.h:1056

References Assert(), compute_query_id, COMPUTE_QUERY_ID_REGRESS, deparse_context_for_plan_tree(), ExplainState::deparse_cxt, ExplainNode(), ExplainPreScanNode(), ExplainPrintSettings(), ExplainPropertyInteger(), ExplainState::hide_workers, IsA, lfirst_node, list_length(), NIL, outerPlanState, QueryDesc::plannedstmt, QueryDesc::planstate, ExplainState::printed_subplans, ps, ExplainState::pstmt, PlannedStmt::queryId, ExplainState::rtable, PlannedStmt::rtable, ExplainState::rtable_names, ExplainState::rtable_size, RTE_GROUP, RangeTblEntry::rtekind, select_rtable_names_for_explain(), UINT64CONST, and ExplainState::verbose.

Referenced by explain_ExecutorEnd(), and ExplainOnePlan().

◆ ExplainPrintSerialize()

static void ExplainPrintSerialize ( ExplainState es,
SerializeMetrics metrics 
)
static

Definition at line 1015 of file explain.c.

1016{
1017 const char *format;
1018
1019 /* We shouldn't get called for EXPLAIN_SERIALIZE_NONE */
1021 format = "text";
1022 else
1023 {
1025 format = "binary";
1026 }
1027
1028 ExplainOpenGroup("Serialization", "Serialization", true, es);
1029
1030 if (es->format == EXPLAIN_FORMAT_TEXT)
1031 {
1033 if (es->timing)
1034 appendStringInfo(es->str, "Serialization: time=%.3f ms output=" UINT64_FORMAT "kB format=%s\n",
1035 1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
1036 BYTES_TO_KILOBYTES(metrics->bytesSent),
1037 format);
1038 else
1039 appendStringInfo(es->str, "Serialization: output=" UINT64_FORMAT "kB format=%s\n",
1040 BYTES_TO_KILOBYTES(metrics->bytesSent),
1041 format);
1042
1043 if (es->buffers && peek_buffer_usage(es, &metrics->bufferUsage))
1044 {
1045 es->indent++;
1046 show_buffer_usage(es, &metrics->bufferUsage);
1047 es->indent--;
1048 }
1049 }
1050 else
1051 {
1052 if (es->timing)
1053 ExplainPropertyFloat("Time", "ms",
1054 1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
1055 3, es);
1056 ExplainPropertyUInteger("Output Volume", "kB",
1057 BYTES_TO_KILOBYTES(metrics->bytesSent), es);
1058 ExplainPropertyText("Format", format, es);
1059 if (es->buffers)
1060 show_buffer_usage(es, &metrics->bufferUsage);
1061 }
1062
1063 ExplainCloseGroup("Serialization", "Serialization", true, es);
1064}
#define UINT64_FORMAT
Definition: c.h:521
#define BYTES_TO_KILOBYTES(b)
Definition: explain.c:63
void ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value, ExplainState *es)
@ EXPLAIN_SERIALIZE_TEXT
Definition: explain_state.h:23
@ EXPLAIN_SERIALIZE_BINARY
Definition: explain_state.h:24
uint64 bytesSent
Definition: explain_dr.h:24
instr_time timeSpent
Definition: explain_dr.h:25
BufferUsage bufferUsage
Definition: explain_dr.h:26

References appendStringInfo(), Assert(), ExplainState::buffers, SerializeMetrics::bufferUsage, BYTES_TO_KILOBYTES, SerializeMetrics::bytesSent, EXPLAIN_FORMAT_TEXT, EXPLAIN_SERIALIZE_BINARY, EXPLAIN_SERIALIZE_TEXT, ExplainCloseGroup(), ExplainIndentText(), ExplainOpenGroup(), ExplainPropertyFloat(), ExplainPropertyText(), ExplainPropertyUInteger(), format, ExplainState::format, ExplainState::indent, INSTR_TIME_GET_DOUBLE, peek_buffer_usage(), ExplainState::serialize, show_buffer_usage(), ExplainState::str, SerializeMetrics::timeSpent, ExplainState::timing, and UINT64_FORMAT.

Referenced by ExplainOnePlan().

◆ ExplainPrintSettings()

static void ExplainPrintSettings ( ExplainState es)
static

Definition at line 701 of file explain.c.

702{
703 int num;
704 struct config_generic **gucs;
705
706 /* bail out if information about settings not requested */
707 if (!es->settings)
708 return;
709
710 /* request an array of relevant settings */
711 gucs = get_explain_guc_options(&num);
712
713 if (es->format != EXPLAIN_FORMAT_TEXT)
714 {
715 ExplainOpenGroup("Settings", "Settings", true, es);
716
717 for (int i = 0; i < num; i++)
718 {
719 char *setting;
720 struct config_generic *conf = gucs[i];
721
722 setting = GetConfigOptionByName(conf->name, NULL, true);
723
724 ExplainPropertyText(conf->name, setting, es);
725 }
726
727 ExplainCloseGroup("Settings", "Settings", true, es);
728 }
729 else
730 {
732
733 /* In TEXT mode, print nothing if there are no options */
734 if (num <= 0)
735 return;
736
738
739 for (int i = 0; i < num; i++)
740 {
741 char *setting;
742 struct config_generic *conf = gucs[i];
743
744 if (i > 0)
746
747 setting = GetConfigOptionByName(conf->name, NULL, true);
748
749 if (setting)
750 appendStringInfo(&str, "%s = '%s'", conf->name, setting);
751 else
752 appendStringInfo(&str, "%s = NULL", conf->name);
753 }
754
755 ExplainPropertyText("Settings", str.data, es);
756 }
757}
char * GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
Definition: guc.c:5432
struct config_generic ** get_explain_guc_options(int *num)
Definition: guc.c:5331
const char * str
const char * name
Definition: guc_tables.h:174

References appendStringInfo(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyText(), ExplainState::format, get_explain_guc_options(), GetConfigOptionByName(), i, initStringInfo(), config_generic::name, ExplainState::settings, and str.

Referenced by ExplainPrintPlan().

◆ ExplainPrintTriggers()

void ExplainPrintTriggers ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 848 of file explain.c.

849{
850 ResultRelInfo *rInfo;
851 bool show_relname;
852 List *resultrels;
853 List *routerels;
854 List *targrels;
855 ListCell *l;
856
857 resultrels = queryDesc->estate->es_opened_result_relations;
858 routerels = queryDesc->estate->es_tuple_routing_result_relations;
859 targrels = queryDesc->estate->es_trig_target_relations;
860
861 ExplainOpenGroup("Triggers", "Triggers", false, es);
862
863 show_relname = (list_length(resultrels) > 1 ||
864 routerels != NIL || targrels != NIL);
865 foreach(l, resultrels)
866 {
867 rInfo = (ResultRelInfo *) lfirst(l);
868 report_triggers(rInfo, show_relname, es);
869 }
870
871 foreach(l, routerels)
872 {
873 rInfo = (ResultRelInfo *) lfirst(l);
874 report_triggers(rInfo, show_relname, es);
875 }
876
877 foreach(l, targrels)
878 {
879 rInfo = (ResultRelInfo *) lfirst(l);
880 report_triggers(rInfo, show_relname, es);
881 }
882
883 ExplainCloseGroup("Triggers", "Triggers", false, es);
884}
static void report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
Definition: explain.c:1108
List * es_tuple_routing_result_relations
Definition: execnodes.h:690
List * es_trig_target_relations
Definition: execnodes.h:693
List * es_opened_result_relations
Definition: execnodes.h:680

References EState::es_opened_result_relations, EState::es_trig_target_relations, EState::es_tuple_routing_result_relations, QueryDesc::estate, ExplainCloseGroup(), ExplainOpenGroup(), lfirst, list_length(), NIL, and report_triggers().

Referenced by explain_ExecutorEnd(), and ExplainOnePlan().

◆ ExplainQuery()

void ExplainQuery ( ParseState pstate,
ExplainStmt stmt,
ParamListInfo  params,
DestReceiver dest 
)

Definition at line 176 of file explain.c.

178{
180 TupOutputState *tstate;
181 JumbleState *jstate = NULL;
182 Query *query;
183 List *rewritten;
184
185 /* Configure the ExplainState based on the provided options */
186 ParseExplainOptionList(es, stmt->options, pstate);
187
188 /* Extract the query and, if enabled, jumble it */
189 query = castNode(Query, stmt->query);
190 if (IsQueryIdEnabled())
191 jstate = JumbleQuery(query);
192
194 (*post_parse_analyze_hook) (pstate, query, jstate);
195
196 /*
197 * Parse analysis was done already, but we still have to run the rule
198 * rewriter. We do not do AcquireRewriteLocks: we assume the query either
199 * came straight from the parser, or suitable locks were acquired by
200 * plancache.c.
201 */
202 rewritten = QueryRewrite(castNode(Query, stmt->query));
203
204 /* emit opening boilerplate */
206
207 if (rewritten == NIL)
208 {
209 /*
210 * In the case of an INSTEAD NOTHING, tell at least that. But in
211 * non-text format, the output is delimited, so this isn't necessary.
212 */
213 if (es->format == EXPLAIN_FORMAT_TEXT)
214 appendStringInfoString(es->str, "Query rewrites to nothing\n");
215 }
216 else
217 {
218 ListCell *l;
219
220 /* Explain every plan */
221 foreach(l, rewritten)
222 {
224 CURSOR_OPT_PARALLEL_OK, NULL, es,
225 pstate, params);
226
227 /* Separate plans with an appropriate separator */
228 if (lnext(rewritten, l) != NULL)
230 }
231 }
232
233 /* emit closing boilerplate */
235 Assert(es->indent == 0);
236
237 /* output tuples */
240 if (es->format == EXPLAIN_FORMAT_TEXT)
241 do_text_output_multiline(tstate, es->str->data);
242 else
243 do_text_output_oneline(tstate, es->str->data);
244 end_tup_output(tstate);
245
246 pfree(es->str->data);
247}
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
void end_tup_output(TupOutputState *tstate)
Definition: execTuples.c:2520
void do_text_output_multiline(TupOutputState *tstate, const char *txt)
Definition: execTuples.c:2490
TupOutputState * begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:2442
#define do_text_output_oneline(tstate, str_to_emit)
Definition: executor.h:609
TupleDesc ExplainResultDesc(ExplainStmt *stmt)
Definition: explain.c:254
void ExplainSeparatePlans(ExplainState *es)
void ExplainEndOutput(ExplainState *es)
void ExplainBeginOutput(ExplainState *es)
ExplainState * NewExplainState(void)
Definition: explain_state.c:61
void ParseExplainOptionList(ExplainState *es, List *options, ParseState *pstate)
Definition: explain_state.c:77
#define stmt
Definition: indent_codes.h:59
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343

References appendStringInfoString(), Assert(), begin_tup_output_tupdesc(), castNode, CURSOR_OPT_PARALLEL_OK, StringInfoData::data, generate_unaccent_rules::dest, do_text_output_multiline(), do_text_output_oneline, end_tup_output(), EXPLAIN_FORMAT_TEXT, ExplainBeginOutput(), ExplainEndOutput(), ExplainOneQuery(), ExplainResultDesc(), ExplainSeparatePlans(), ExplainState::format, ExplainState::indent, IsQueryIdEnabled(), JumbleQuery(), lfirst_node, lnext(), NewExplainState(), NIL, ParseExplainOptionList(), pfree(), post_parse_analyze_hook, QueryRewrite(), stmt, ExplainState::str, and TTSOpsVirtual.

Referenced by standard_ProcessUtility().

◆ ExplainQueryParameters()

void ExplainQueryParameters ( ExplainState es,
ParamListInfo  params,
int  maxlen 
)

Definition at line 1090 of file explain.c.

1091{
1092 char *str;
1093
1094 /* This check is consistent with errdetail_params() */
1095 if (params == NULL || params->numParams <= 0 || maxlen == 0)
1096 return;
1097
1098 str = BuildParamLogString(params, NULL, maxlen);
1099 if (str && str[0] != '\0')
1100 ExplainPropertyText("Query Parameters", str, es);
1101}
char * BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen)
Definition: params.c:335

References BuildParamLogString(), ExplainPropertyText(), ParamListInfoData::numParams, and str.

Referenced by explain_ExecutorEnd().

◆ ExplainQueryText()

void ExplainQueryText ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 1075 of file explain.c.

1076{
1077 if (queryDesc->sourceText)
1078 ExplainPropertyText("Query Text", queryDesc->sourceText, es);
1079}
const char * sourceText
Definition: execdesc.h:39

References ExplainPropertyText(), and QueryDesc::sourceText.

Referenced by explain_ExecutorEnd().

◆ ExplainResultDesc()

TupleDesc ExplainResultDesc ( ExplainStmt stmt)

Definition at line 254 of file explain.c.

255{
256 TupleDesc tupdesc;
257 ListCell *lc;
258 Oid result_type = TEXTOID;
259
260 /* Check for XML format option */
261 foreach(lc, stmt->options)
262 {
263 DefElem *opt = (DefElem *) lfirst(lc);
264
265 if (strcmp(opt->defname, "format") == 0)
266 {
267 char *p = defGetString(opt);
268
269 if (strcmp(p, "xml") == 0)
270 result_type = XMLOID;
271 else if (strcmp(p, "json") == 0)
272 result_type = JSONOID;
273 else
274 result_type = TEXTOID;
275 /* don't "break", as ExplainQuery will use the last value */
276 }
277 }
278
279 /* Need a tuple descriptor representing a single TEXT or XML column */
280 tupdesc = CreateTemplateTupleDesc(1);
281 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
282 result_type, -1, 0);
283 return tupdesc;
284}
int16 AttrNumber
Definition: attnum.h:21
char * defGetString(DefElem *def)
Definition: define.c:35
unsigned int Oid
Definition: postgres_ext.h:32
char * defname
Definition: parsenodes.h:826
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:164
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:801

References CreateTemplateTupleDesc(), defGetString(), DefElem::defname, lfirst, stmt, and TupleDescInitEntry().

Referenced by ExplainQuery(), and UtilityTupleDescriptor().

◆ ExplainScanTarget()

static void ExplainScanTarget ( Scan plan,
ExplainState es 
)
static

Definition at line 4372 of file explain.c.

4373{
4374 ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
4375}

References ExplainTargetRel(), and plan.

Referenced by ExplainNode().

◆ ExplainSubPlans()

static void ExplainSubPlans ( List plans,
List ancestors,
const char *  relationship,
ExplainState es 
)
static

Definition at line 4786 of file explain.c.

4788{
4789 ListCell *lst;
4790
4791 foreach(lst, plans)
4792 {
4793 SubPlanState *sps = (SubPlanState *) lfirst(lst);
4794 SubPlan *sp = sps->subplan;
4795
4796 /*
4797 * There can be multiple SubPlan nodes referencing the same physical
4798 * subplan (same plan_id, which is its index in PlannedStmt.subplans).
4799 * We should print a subplan only once, so track which ones we already
4800 * printed. This state must be global across the plan tree, since the
4801 * duplicate nodes could be in different plan nodes, eg both a bitmap
4802 * indexscan's indexqual and its parent heapscan's recheck qual. (We
4803 * do not worry too much about which plan node we show the subplan as
4804 * attached to in such cases.)
4805 */
4807 continue;
4809 sp->plan_id);
4810
4811 /*
4812 * Treat the SubPlan node as an ancestor of the plan node(s) within
4813 * it, so that ruleutils.c can find the referents of subplan
4814 * parameters.
4815 */
4816 ancestors = lcons(sp, ancestors);
4817
4818 ExplainNode(sps->planstate, ancestors,
4819 relationship, sp->plan_name, es);
4820
4821 ancestors = list_delete_first(ancestors);
4822 }
4823}
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
struct PlanState * planstate
Definition: execnodes.h:1000
SubPlan * subplan
Definition: execnodes.h:999
int plan_id
Definition: primnodes.h:1087
char * plan_name
Definition: primnodes.h:1089

References bms_add_member(), bms_is_member(), ExplainNode(), lcons(), lfirst, list_delete_first(), SubPlan::plan_id, SubPlan::plan_name, SubPlanState::planstate, ExplainState::printed_subplans, and SubPlanState::subplan.

Referenced by ExplainNode().

◆ ExplainTargetRel()

static void ExplainTargetRel ( Plan plan,
Index  rti,
ExplainState es 
)
static

Definition at line 4394 of file explain.c.

4395{
4396 char *objectname = NULL;
4397 char *namespace = NULL;
4398 const char *objecttag = NULL;
4399 RangeTblEntry *rte;
4400 char *refname;
4401
4402 rte = rt_fetch(rti, es->rtable);
4403 refname = (char *) list_nth(es->rtable_names, rti - 1);
4404 if (refname == NULL)
4405 refname = rte->eref->aliasname;
4406
4407 switch (nodeTag(plan))
4408 {
4409 case T_SeqScan:
4410 case T_SampleScan:
4411 case T_IndexScan:
4412 case T_IndexOnlyScan:
4413 case T_BitmapHeapScan:
4414 case T_TidScan:
4415 case T_TidRangeScan:
4416 case T_ForeignScan:
4417 case T_CustomScan:
4418 case T_ModifyTable:
4419 /* Assert it's on a real relation */
4420 Assert(rte->rtekind == RTE_RELATION);
4421 objectname = get_rel_name(rte->relid);
4422 if (es->verbose)
4423 namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid));
4424 objecttag = "Relation Name";
4425 break;
4426 case T_FunctionScan:
4427 {
4428 FunctionScan *fscan = (FunctionScan *) plan;
4429
4430 /* Assert it's on a RangeFunction */
4431 Assert(rte->rtekind == RTE_FUNCTION);
4432
4433 /*
4434 * If the expression is still a function call of a single
4435 * function, we can get the real name of the function.
4436 * Otherwise, punt. (Even if it was a single function call
4437 * originally, the optimizer could have simplified it away.)
4438 */
4439 if (list_length(fscan->functions) == 1)
4440 {
4442
4443 if (IsA(rtfunc->funcexpr, FuncExpr))
4444 {
4445 FuncExpr *funcexpr = (FuncExpr *) rtfunc->funcexpr;
4446 Oid funcid = funcexpr->funcid;
4447
4448 objectname = get_func_name(funcid);
4449 if (es->verbose)
4451 }
4452 }
4453 objecttag = "Function Name";
4454 }
4455 break;
4456 case T_TableFuncScan:
4457 {
4458 TableFunc *tablefunc = ((TableFuncScan *) plan)->tablefunc;
4459
4460 Assert(rte->rtekind == RTE_TABLEFUNC);
4461 switch (tablefunc->functype)
4462 {
4463 case TFT_XMLTABLE:
4464 objectname = "xmltable";
4465 break;
4466 case TFT_JSON_TABLE:
4467 objectname = "json_table";
4468 break;
4469 default:
4470 elog(ERROR, "invalid TableFunc type %d",
4471 (int) tablefunc->functype);
4472 }
4473 objecttag = "Table Function Name";
4474 }
4475 break;
4476 case T_ValuesScan:
4477 Assert(rte->rtekind == RTE_VALUES);
4478 break;
4479 case T_CteScan:
4480 /* Assert it's on a non-self-reference CTE */
4481 Assert(rte->rtekind == RTE_CTE);
4482 Assert(!rte->self_reference);
4483 objectname = rte->ctename;
4484 objecttag = "CTE Name";
4485 break;
4486 case T_NamedTuplestoreScan:
4488 objectname = rte->enrname;
4489 objecttag = "Tuplestore Name";
4490 break;
4491 case T_WorkTableScan:
4492 /* Assert it's on a self-reference CTE */
4493 Assert(rte->rtekind == RTE_CTE);
4494 Assert(rte->self_reference);
4495 objectname = rte->ctename;
4496 objecttag = "CTE Name";
4497 break;
4498 default:
4499 break;
4500 }
4501
4502 if (es->format == EXPLAIN_FORMAT_TEXT)
4503 {
4504 appendStringInfoString(es->str, " on");
4505 if (namespace != NULL)
4506 appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
4507 quote_identifier(objectname));
4508 else if (objectname != NULL)
4509 appendStringInfo(es->str, " %s", quote_identifier(objectname));
4510 if (objectname == NULL || strcmp(refname, objectname) != 0)
4511 appendStringInfo(es->str, " %s", quote_identifier(refname));
4512 }
4513 else
4514 {
4515 if (objecttag != NULL && objectname != NULL)
4516 ExplainPropertyText(objecttag, objectname, es);
4517 if (namespace != NULL)
4518 ExplainPropertyText("Schema", namespace, es);
4519 ExplainPropertyText("Alias", refname, es);
4520 }
4521}
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:2035
char * get_namespace_name_or_temp(Oid nspid)
Definition: lsyscache.c:3473
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1691
Oid get_func_namespace(Oid funcid)
Definition: lsyscache.c:1715
@ RTE_CTE
Definition: parsenodes.h:1032
@ RTE_NAMEDTUPLESTORE
Definition: parsenodes.h:1033
@ RTE_VALUES
Definition: parsenodes.h:1031
@ RTE_FUNCTION
Definition: parsenodes.h:1029
@ RTE_TABLEFUNC
Definition: parsenodes.h:1030
@ RTE_RELATION
Definition: parsenodes.h:1026
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
@ TFT_XMLTABLE
Definition: primnodes.h:100
@ TFT_JSON_TABLE
Definition: primnodes.h:101
Oid funcid
Definition: primnodes.h:767
List * functions
Definition: plannodes.h:724
char * ctename
Definition: parsenodes.h:1205
char * enrname
Definition: parsenodes.h:1240
TableFuncType functype
Definition: primnodes.h:114

References appendStringInfo(), appendStringInfoString(), Assert(), elog, ERROR, EXPLAIN_FORMAT_TEXT, ExplainPropertyText(), ExplainState::format, RangeTblFunction::funcexpr, FuncExpr::funcid, FunctionScan::functions, TableFunc::functype, get_func_name(), get_func_namespace(), get_namespace_name_or_temp(), get_rel_name(), get_rel_namespace(), IsA, linitial, list_length(), list_nth(), nodeTag, plan, quote_identifier(), RangeTblEntry::relid, rt_fetch, ExplainState::rtable, ExplainState::rtable_names, RTE_CTE, RTE_FUNCTION, RTE_NAMEDTUPLESTORE, RTE_RELATION, RTE_TABLEFUNC, RTE_VALUES, RangeTblEntry::rtekind, ExplainState::str, TFT_JSON_TABLE, TFT_XMLTABLE, and ExplainState::verbose.

Referenced by ExplainModifyTarget(), ExplainScanTarget(), and show_modifytable_info().

◆ peek_buffer_usage()

static bool peek_buffer_usage ( ExplainState es,
const BufferUsage usage 
)
static

Definition at line 4058 of file explain.c.

4059{
4060 bool has_shared;
4061 bool has_local;
4062 bool has_temp;
4063 bool has_shared_timing;
4064 bool has_local_timing;
4065 bool has_temp_timing;
4066
4067 if (usage == NULL)
4068 return false;
4069
4070 if (es->format != EXPLAIN_FORMAT_TEXT)
4071 return true;
4072
4073 has_shared = (usage->shared_blks_hit > 0 ||
4074 usage->shared_blks_read > 0 ||
4075 usage->shared_blks_dirtied > 0 ||
4076 usage->shared_blks_written > 0);
4077 has_local = (usage->local_blks_hit > 0 ||
4078 usage->local_blks_read > 0 ||
4079 usage->local_blks_dirtied > 0 ||
4080 usage->local_blks_written > 0);
4081 has_temp = (usage->temp_blks_read > 0 ||
4082 usage->temp_blks_written > 0);
4083 has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
4084 !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
4085 has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
4086 !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
4087 has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
4088 !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
4089
4090 return has_shared || has_local || has_temp || has_shared_timing ||
4091 has_local_timing || has_temp_timing;
4092}
#define INSTR_TIME_IS_ZERO(t)
Definition: instr_time.h:169
static void usage(const char *progname)
Definition: vacuumlo.c:414

References EXPLAIN_FORMAT_TEXT, ExplainState::format, INSTR_TIME_IS_ZERO, and usage().

Referenced by ExplainOnePlan(), and ExplainPrintSerialize().

◆ plan_is_disabled()

static bool plan_is_disabled ( Plan plan)
static

Definition at line 1258 of file explain.c.

1259{
1260 int child_disabled_nodes;
1261
1262 /* The node is certainly not disabled if this is zero */
1263 if (plan->disabled_nodes == 0)
1264 return false;
1265
1266 child_disabled_nodes = 0;
1267
1268 /*
1269 * Handle special nodes first. Children of BitmapOrs and BitmapAnds can't
1270 * be disabled, so no need to handle those specifically.
1271 */
1272 if (IsA(plan, Append))
1273 {
1274 ListCell *lc;
1275 Append *aplan = (Append *) plan;
1276
1277 /*
1278 * Sum the Append childrens' disabled_nodes. This purposefully
1279 * includes any run-time pruned children. Ignoring those could give
1280 * us the incorrect number of disabled nodes.
1281 */
1282 foreach(lc, aplan->appendplans)
1283 {
1284 Plan *subplan = lfirst(lc);
1285
1286 child_disabled_nodes += subplan->disabled_nodes;
1287 }
1288 }
1289 else if (IsA(plan, MergeAppend))
1290 {
1291 ListCell *lc;
1292 MergeAppend *maplan = (MergeAppend *) plan;
1293
1294 /*
1295 * Sum the MergeAppend childrens' disabled_nodes. This purposefully
1296 * includes any run-time pruned children. Ignoring those could give
1297 * us the incorrect number of disabled nodes.
1298 */
1299 foreach(lc, maplan->mergeplans)
1300 {
1301 Plan *subplan = lfirst(lc);
1302
1303 child_disabled_nodes += subplan->disabled_nodes;
1304 }
1305 }
1306 else if (IsA(plan, SubqueryScan))
1307 child_disabled_nodes += ((SubqueryScan *) plan)->subplan->disabled_nodes;
1308 else if (IsA(plan, CustomScan))
1309 {
1310 ListCell *lc;
1311 CustomScan *cplan = (CustomScan *) plan;
1312
1313 foreach(lc, cplan->custom_plans)
1314 {
1315 Plan *subplan = lfirst(lc);
1316
1317 child_disabled_nodes += subplan->disabled_nodes;
1318 }
1319 }
1320 else
1321 {
1322 /*
1323 * Else, sum up disabled_nodes from the plan's inner and outer side.
1324 */
1325 if (outerPlan(plan))
1326 child_disabled_nodes += outerPlan(plan)->disabled_nodes;
1327 if (innerPlan(plan))
1328 child_disabled_nodes += innerPlan(plan)->disabled_nodes;
1329 }
1330
1331 /*
1332 * It's disabled if the plan's disabled_nodes is higher than the sum of
1333 * its child's plan disabled_nodes.
1334 */
1335 if (plan->disabled_nodes > child_disabled_nodes)
1336 return true;
1337
1338 return false;
1339}
#define innerPlan(node)
Definition: plannodes.h:237
#define outerPlan(node)
Definition: plannodes.h:238
List * appendplans
Definition: plannodes.h:350
List * custom_plans
Definition: plannodes.h:874
List * mergeplans
Definition: plannodes.h:380
int disabled_nodes
Definition: plannodes.h:172

References Append::appendplans, CustomScan::custom_plans, Plan::disabled_nodes, innerPlan, IsA, lfirst, MergeAppend::mergeplans, outerPlan, and plan.

Referenced by ExplainNode().

◆ report_triggers()

static void report_triggers ( ResultRelInfo rInfo,
bool  show_relname,
ExplainState es 
)
static

Definition at line 1108 of file explain.c.

1109{
1110 int nt;
1111
1112 if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
1113 return;
1114 for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
1115 {
1116 Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
1117 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
1118 char *relname;
1119 char *conname = NULL;
1120
1121 /* Must clean up instrumentation state */
1122 InstrEndLoop(instr);
1123
1124 /*
1125 * We ignore triggers that were never invoked; they likely aren't
1126 * relevant to the current query type.
1127 */
1128 if (instr->ntuples == 0)
1129 continue;
1130
1131 ExplainOpenGroup("Trigger", NULL, true, es);
1132
1134 if (OidIsValid(trig->tgconstraint))
1135 conname = get_constraint_name(trig->tgconstraint);
1136
1137 /*
1138 * In text format, we avoid printing both the trigger name and the
1139 * constraint name unless VERBOSE is specified. In non-text formats
1140 * we just print everything.
1141 */
1142 if (es->format == EXPLAIN_FORMAT_TEXT)
1143 {
1144 if (es->verbose || conname == NULL)
1145 appendStringInfo(es->str, "Trigger %s", trig->tgname);
1146 else
1147 appendStringInfoString(es->str, "Trigger");
1148 if (conname)
1149 appendStringInfo(es->str, " for constraint %s", conname);
1150 if (show_relname)
1151 appendStringInfo(es->str, " on %s", relname);
1152 if (es->timing)
1153 appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
1154 1000.0 * instr->total, instr->ntuples);
1155 else
1156 appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
1157 }
1158 else
1159 {
1160 ExplainPropertyText("Trigger Name", trig->tgname, es);
1161 if (conname)
1162 ExplainPropertyText("Constraint Name", conname, es);
1163 ExplainPropertyText("Relation", relname, es);
1164 if (es->timing)
1165 ExplainPropertyFloat("Time", "ms", 1000.0 * instr->total, 3,
1166 es);
1167 ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
1168 }
1169
1170 if (conname)
1171 pfree(conname);
1172
1173 ExplainCloseGroup("Trigger", NULL, true, es);
1174 }
1175}
#define OidIsValid(objectId)
Definition: c.h:746
char * get_constraint_name(Oid conoid)
Definition: lsyscache.c:1116
NameData relname
Definition: pg_class.h:38
#define RelationGetRelationName(relation)
Definition: rel.h:547
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:519
Relation ri_RelationDesc
Definition: execnodes.h:475
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:510
int numtriggers
Definition: reltrigger.h:50
Trigger * triggers
Definition: reltrigger.h:49
Oid tgconstraint
Definition: reltrigger.h:35
char * tgname
Definition: reltrigger.h:27

References appendStringInfo(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyFloat(), ExplainPropertyText(), ExplainState::format, get_constraint_name(), InstrEndLoop(), Instrumentation::ntuples, TriggerDesc::numtriggers, OidIsValid, pfree(), RelationGetRelationName, relname, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigInstrument, ExplainState::str, Trigger::tgconstraint, Trigger::tgname, ExplainState::timing, Instrumentation::total, TriggerDesc::triggers, and ExplainState::verbose.

Referenced by ExplainPrintTriggers().

◆ show_agg_keys()

static void show_agg_keys ( AggState astate,
List ancestors,
ExplainState es 
)
static

Definition at line 2628 of file explain.c.

2630{
2631 Agg *plan = (Agg *) astate->ss.ps.plan;
2632
2633 if (plan->numCols > 0 || plan->groupingSets)
2634 {
2635 /* The key columns refer to the tlist of the child plan */
2636 ancestors = lcons(plan, ancestors);
2637
2638 if (plan->groupingSets)
2639 show_grouping_sets(outerPlanState(astate), plan, ancestors, es);
2640 else
2641 show_sort_group_keys(outerPlanState(astate), "Group Key",
2642 plan->numCols, 0, plan->grpColIdx,
2643 NULL, NULL, NULL,
2644 ancestors, es);
2645
2646 ancestors = list_delete_first(ancestors);
2647 }
2648}
static void show_sort_group_keys(PlanState *planstate, const char *qlabel, int nkeys, int nPresortedKeys, AttrNumber *keycols, Oid *sortOperators, Oid *collations, bool *nullsFirst, List *ancestors, ExplainState *es)
Definition: explain.c:2780
static void show_grouping_sets(PlanState *planstate, Agg *agg, List *ancestors, ExplainState *es)
Definition: explain.c:2651
ScanState ss
Definition: execnodes.h:2521
PlanState ps
Definition: execnodes.h:1609

References if(), lcons(), list_delete_first(), outerPlanState, PlanState::plan, plan, ScanState::ps, show_grouping_sets(), show_sort_group_keys(), and AggState::ss.

Referenced by ExplainNode().

◆ show_buffer_usage()

static void show_buffer_usage ( ExplainState es,
const BufferUsage usage 
)
static

Definition at line 4098 of file explain.c.

4099{
4100 if (es->format == EXPLAIN_FORMAT_TEXT)
4101 {
4102 bool has_shared = (usage->shared_blks_hit > 0 ||
4103 usage->shared_blks_read > 0 ||
4104 usage->shared_blks_dirtied > 0 ||
4105 usage->shared_blks_written > 0);
4106 bool has_local = (usage->local_blks_hit > 0 ||
4107 usage->local_blks_read > 0 ||
4108 usage->local_blks_dirtied > 0 ||
4109 usage->local_blks_written > 0);
4110 bool has_temp = (usage->temp_blks_read > 0 ||
4111 usage->temp_blks_written > 0);
4112 bool has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
4113 !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
4114 bool has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
4115 !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
4116 bool has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
4117 !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
4118
4119 /* Show only positive counter values. */
4120 if (has_shared || has_local || has_temp)
4121 {
4123 appendStringInfoString(es->str, "Buffers:");
4124
4125 if (has_shared)
4126 {
4127 appendStringInfoString(es->str, " shared");
4128 if (usage->shared_blks_hit > 0)
4129 appendStringInfo(es->str, " hit=%lld",
4130 (long long) usage->shared_blks_hit);
4131 if (usage->shared_blks_read > 0)
4132 appendStringInfo(es->str, " read=%lld",
4133 (long long) usage->shared_blks_read);
4134 if (usage->shared_blks_dirtied > 0)
4135 appendStringInfo(es->str, " dirtied=%lld",
4136 (long long) usage->shared_blks_dirtied);
4137 if (usage->shared_blks_written > 0)
4138 appendStringInfo(es->str, " written=%lld",
4139 (long long) usage->shared_blks_written);
4140 if (has_local || has_temp)
4141 appendStringInfoChar(es->str, ',');
4142 }
4143 if (has_local)
4144 {
4145 appendStringInfoString(es->str, " local");
4146 if (usage->local_blks_hit > 0)
4147 appendStringInfo(es->str, " hit=%lld",
4148 (long long) usage->local_blks_hit);
4149 if (usage->local_blks_read > 0)
4150 appendStringInfo(es->str, " read=%lld",
4151 (long long) usage->local_blks_read);
4152 if (usage->local_blks_dirtied > 0)
4153 appendStringInfo(es->str, " dirtied=%lld",
4154 (long long) usage->local_blks_dirtied);
4155 if (usage->local_blks_written > 0)
4156 appendStringInfo(es->str, " written=%lld",
4157 (long long) usage->local_blks_written);
4158 if (has_temp)
4159 appendStringInfoChar(es->str, ',');
4160 }
4161 if (has_temp)
4162 {
4163 appendStringInfoString(es->str, " temp");
4164 if (usage->temp_blks_read > 0)
4165 appendStringInfo(es->str, " read=%lld",
4166 (long long) usage->temp_blks_read);
4167 if (usage->temp_blks_written > 0)
4168 appendStringInfo(es->str, " written=%lld",
4169 (long long) usage->temp_blks_written);
4170 }
4171 appendStringInfoChar(es->str, '\n');
4172 }
4173
4174 /* As above, show only positive counter values. */
4175 if (has_shared_timing || has_local_timing || has_temp_timing)
4176 {
4178 appendStringInfoString(es->str, "I/O Timings:");
4179
4180 if (has_shared_timing)
4181 {
4182 appendStringInfoString(es->str, " shared");
4183 if (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time))
4184 appendStringInfo(es->str, " read=%0.3f",
4185 INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time));
4186 if (!INSTR_TIME_IS_ZERO(usage->shared_blk_write_time))
4187 appendStringInfo(es->str, " write=%0.3f",
4188 INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time));
4189 if (has_local_timing || has_temp_timing)
4190 appendStringInfoChar(es->str, ',');
4191 }
4192 if (has_local_timing)
4193 {
4194 appendStringInfoString(es->str, " local");
4195 if (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time))
4196 appendStringInfo(es->str, " read=%0.3f",
4197 INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time));
4198 if (!INSTR_TIME_IS_ZERO(usage->local_blk_write_time))
4199 appendStringInfo(es->str, " write=%0.3f",
4200 INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time));
4201 if (has_temp_timing)
4202 appendStringInfoChar(es->str, ',');
4203 }
4204 if (has_temp_timing)
4205 {
4206 appendStringInfoString(es->str, " temp");
4207 if (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time))
4208 appendStringInfo(es->str, " read=%0.3f",
4209 INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time));
4210 if (!INSTR_TIME_IS_ZERO(usage->temp_blk_write_time))
4211 appendStringInfo(es->str, " write=%0.3f",
4212 INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time));
4213 }
4214 appendStringInfoChar(es->str, '\n');
4215 }
4216 }
4217 else
4218 {
4219 ExplainPropertyInteger("Shared Hit Blocks", NULL,
4220 usage->shared_blks_hit, es);
4221 ExplainPropertyInteger("Shared Read Blocks", NULL,
4222 usage->shared_blks_read, es);
4223 ExplainPropertyInteger("Shared Dirtied Blocks", NULL,
4224 usage->shared_blks_dirtied, es);
4225 ExplainPropertyInteger("Shared Written Blocks", NULL,
4226 usage->shared_blks_written, es);
4227 ExplainPropertyInteger("Local Hit Blocks", NULL,
4228 usage->local_blks_hit, es);
4229 ExplainPropertyInteger("Local Read Blocks", NULL,
4230 usage->local_blks_read, es);
4231 ExplainPropertyInteger("Local Dirtied Blocks", NULL,
4232 usage->local_blks_dirtied, es);
4233 ExplainPropertyInteger("Local Written Blocks", NULL,
4234 usage->local_blks_written, es);
4235 ExplainPropertyInteger("Temp Read Blocks", NULL,
4236 usage->temp_blks_read, es);
4237 ExplainPropertyInteger("Temp Written Blocks", NULL,
4238 usage->temp_blks_written, es);
4239 if (track_io_timing)
4240 {
4241 ExplainPropertyFloat("Shared I/O Read Time", "ms",
4242 INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time),
4243 3, es);
4244 ExplainPropertyFloat("Shared I/O Write Time", "ms",
4245 INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time),
4246 3, es);
4247 ExplainPropertyFloat("Local I/O Read Time", "ms",
4248 INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time),
4249 3, es);
4250 ExplainPropertyFloat("Local I/O Write Time", "ms",
4251 INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time),
4252 3, es);
4253 ExplainPropertyFloat("Temp I/O Read Time", "ms",
4254 INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time),
4255 3, es);
4256 ExplainPropertyFloat("Temp I/O Write Time", "ms",
4257 INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time),
4258 3, es);
4259 }
4260 }
4261}
bool track_io_timing
Definition: bufmgr.c:143
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:191

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainState::format, INSTR_TIME_GET_MILLISEC, INSTR_TIME_IS_ZERO, ExplainState::str, track_io_timing, and usage().

Referenced by ExplainNode(), ExplainOnePlan(), and ExplainPrintSerialize().

◆ show_ctescan_info()

static void show_ctescan_info ( CteScanState ctescanstate,
ExplainState es 
)
static

Definition at line 3525 of file explain.c.

3526{
3527 char *maxStorageType;
3528 int64 maxSpaceUsed;
3529
3530 Tuplestorestate *tupstore = ctescanstate->leader->cte_table;
3531
3532 if (!es->analyze || tupstore == NULL)
3533 return;
3534
3535 tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
3536 show_storage_info(maxStorageType, maxSpaceUsed, es);
3537}
static void show_storage_info(char *maxStorageType, int64 maxSpaceUsed, ExplainState *es)
Definition: explain.c:3007
Tuplestorestate * cte_table
Definition: execnodes.h:2055
struct CteScanState * leader
Definition: execnodes.h:2053
void tuplestore_get_stats(Tuplestorestate *state, char **max_storage_type, int64 *max_space)
Definition: tuplestore.c:1533

References ExplainState::analyze, CteScanState::cte_table, CteScanState::leader, show_storage_info(), and tuplestore_get_stats().

Referenced by ExplainNode().

◆ show_expression()

static void show_expression ( Node node,
const char *  qlabel,
PlanState planstate,
List ancestors,
bool  useprefix,
ExplainState es 
)
static

Definition at line 2508 of file explain.c.

2511{
2512 List *context;
2513 char *exprstr;
2514
2515 /* Set up deparsing context */
2517 planstate->plan,
2518 ancestors);
2519
2520 /* Deparse the expression */
2521 exprstr = deparse_expression(node, context, useprefix, false);
2522
2523 /* And add to es->str */
2524 ExplainPropertyText(qlabel, exprstr, es);
2525}
List * set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
Definition: ruleutils.c:3825
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3645

References config_generic::context, ExplainState::deparse_cxt, deparse_expression(), ExplainPropertyText(), PlanState::plan, and set_deparse_context_plan().

Referenced by ExplainNode(), and show_qual().

◆ show_foreignscan_info()

static void show_foreignscan_info ( ForeignScanState fsstate,
ExplainState es 
)
static

Definition at line 4006 of file explain.c.

4007{
4008 FdwRoutine *fdwroutine = fsstate->fdwroutine;
4009
4010 /* Let the FDW emit whatever fields it wants */
4011 if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
4012 {
4013 if (fdwroutine->ExplainDirectModify != NULL)
4014 fdwroutine->ExplainDirectModify(fsstate, es);
4015 }
4016 else
4017 {
4018 if (fdwroutine->ExplainForeignScan != NULL)
4019 fdwroutine->ExplainForeignScan(fsstate, es);
4020 }
4021}
ExplainForeignScan_function ExplainForeignScan
Definition: fdwapi.h:252
ExplainDirectModify_function ExplainDirectModify
Definition: fdwapi.h:254
struct FdwRoutine * fdwroutine
Definition: execnodes.h:2104
ScanState ss
Definition: execnodes.h:2099

References CMD_SELECT, FdwRoutine::ExplainDirectModify, FdwRoutine::ExplainForeignScan, ForeignScanState::fdwroutine, PlanState::plan, ScanState::ps, and ForeignScanState::ss.

Referenced by ExplainNode().

◆ show_group_keys()

static void show_group_keys ( GroupState gstate,
List ancestors,
ExplainState es 
)
static

Definition at line 2760 of file explain.c.

2762{
2763 Group *plan = (Group *) gstate->ss.ps.plan;
2764
2765 /* The key columns refer to the tlist of the child plan */
2766 ancestors = lcons(plan, ancestors);
2767 show_sort_group_keys(outerPlanState(gstate), "Group Key",
2768 plan->numCols, 0, plan->grpColIdx,
2769 NULL, NULL, NULL,
2770 ancestors, es);
2771 ancestors = list_delete_first(ancestors);
2772}
ScanState ss
Definition: execnodes.h:2474

References lcons(), list_delete_first(), outerPlanState, PlanState::plan, plan, ScanState::ps, show_sort_group_keys(), and GroupState::ss.

Referenced by ExplainNode().

◆ show_grouping_set_keys()

static void show_grouping_set_keys ( PlanState planstate,
Agg aggnode,
Sort sortnode,
List context,
bool  useprefix,
List ancestors,
ExplainState es 
)
static

Definition at line 2682 of file explain.c.

2686{
2687 Plan *plan = planstate->plan;
2688 char *exprstr;
2689 ListCell *lc;
2690 List *gsets = aggnode->groupingSets;
2691 AttrNumber *keycols = aggnode->grpColIdx;
2692 const char *keyname;
2693 const char *keysetname;
2694
2695 if (aggnode->aggstrategy == AGG_HASHED || aggnode->aggstrategy == AGG_MIXED)
2696 {
2697 keyname = "Hash Key";
2698 keysetname = "Hash Keys";
2699 }
2700 else
2701 {
2702 keyname = "Group Key";
2703 keysetname = "Group Keys";
2704 }
2705
2706 ExplainOpenGroup("Grouping Set", NULL, true, es);
2707
2708 if (sortnode)
2709 {
2710 show_sort_group_keys(planstate, "Sort Key",
2711 sortnode->numCols, 0, sortnode->sortColIdx,
2712 sortnode->sortOperators, sortnode->collations,
2713 sortnode->nullsFirst,
2714 ancestors, es);
2715 if (es->format == EXPLAIN_FORMAT_TEXT)
2716 es->indent++;
2717 }
2718
2719 ExplainOpenGroup(keysetname, keysetname, false, es);
2720
2721 foreach(lc, gsets)
2722 {
2723 List *result = NIL;
2724 ListCell *lc2;
2725
2726 foreach(lc2, (List *) lfirst(lc))
2727 {
2728 Index i = lfirst_int(lc2);
2729 AttrNumber keyresno = keycols[i];
2730 TargetEntry *target = get_tle_by_resno(plan->targetlist,
2731 keyresno);
2732
2733 if (!target)
2734 elog(ERROR, "no tlist entry for key %d", keyresno);
2735 /* Deparse the expression, showing any top-level cast */
2736 exprstr = deparse_expression((Node *) target->expr, context,
2737 useprefix, true);
2738
2739 result = lappend(result, exprstr);
2740 }
2741
2742 if (!result && es->format == EXPLAIN_FORMAT_TEXT)
2743 ExplainPropertyText(keyname, "()", es);
2744 else
2745 ExplainPropertyListNested(keyname, result, es);
2746 }
2747
2748 ExplainCloseGroup(keysetname, keysetname, false, es);
2749
2750 if (sortnode && es->format == EXPLAIN_FORMAT_TEXT)
2751 es->indent--;
2752
2753 ExplainCloseGroup("Grouping Set", NULL, true, es);
2754}
unsigned int Index
Definition: c.h:585
void ExplainPropertyListNested(const char *qlabel, List *data, ExplainState *es)
TargetEntry * get_tle_by_resno(List *tlist, AttrNumber resno)
#define lfirst_int(lc)
Definition: pg_list.h:173
List * groupingSets
Definition: plannodes.h:1167
int numCols
Definition: plannodes.h:1074
Expr * expr
Definition: primnodes.h:2219

References AGG_HASHED, AGG_MIXED, Agg::aggstrategy, config_generic::context, deparse_expression(), elog, ERROR, EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyListNested(), ExplainPropertyText(), TargetEntry::expr, ExplainState::format, get_tle_by_resno(), Agg::groupingSets, i, ExplainState::indent, lappend(), lfirst, lfirst_int, NIL, Sort::numCols, PlanState::plan, plan, and show_sort_group_keys().

Referenced by show_grouping_sets().

◆ show_grouping_sets()

static void show_grouping_sets ( PlanState planstate,
Agg agg,
List ancestors,
ExplainState es 
)
static

Definition at line 2651 of file explain.c.

2653{
2654 List *context;
2655 bool useprefix;
2656 ListCell *lc;
2657
2658 /* Set up deparsing context */
2660 planstate->plan,
2661 ancestors);
2662 useprefix = (es->rtable_size > 1 || es->verbose);
2663
2664 ExplainOpenGroup("Grouping Sets", "Grouping Sets", false, es);
2665
2666 show_grouping_set_keys(planstate, agg, NULL,
2667 context, useprefix, ancestors, es);
2668
2669 foreach(lc, agg->chain)
2670 {
2671 Agg *aggnode = lfirst(lc);
2672 Sort *sortnode = (Sort *) aggnode->plan.lefttree;
2673
2674 show_grouping_set_keys(planstate, aggnode, sortnode,
2675 context, useprefix, ancestors, es);
2676 }
2677
2678 ExplainCloseGroup("Grouping Sets", "Grouping Sets", false, es);
2679}
static void show_grouping_set_keys(PlanState *planstate, Agg *aggnode, Sort *sortnode, List *context, bool useprefix, List *ancestors, ExplainState *es)
Definition: explain.c:2682
List * chain
Definition: plannodes.h:1170
Plan plan
Definition: plannodes.h:1137
struct Plan * lefttree
Definition: plannodes.h:210

References Agg::chain, config_generic::context, ExplainState::deparse_cxt, ExplainCloseGroup(), ExplainOpenGroup(), Plan::lefttree, lfirst, PlanState::plan, Agg::plan, ExplainState::rtable_size, set_deparse_context_plan(), show_grouping_set_keys(), and ExplainState::verbose.

Referenced by show_agg_keys().

◆ show_hash_info()

static void show_hash_info ( HashState hashstate,
ExplainState es 
)
static

Definition at line 3387 of file explain.c.

3388{
3389 HashInstrumentation hinstrument = {0};
3390
3391 /*
3392 * Collect stats from the local process, even when it's a parallel query.
3393 * In a parallel query, the leader process may or may not have run the
3394 * hash join, and even if it did it may not have built a hash table due to
3395 * timing (if it started late it might have seen no tuples in the outer
3396 * relation and skipped building the hash table). Therefore we have to be
3397 * prepared to get instrumentation data from all participants.
3398 */
3399 if (hashstate->hinstrument)
3400 memcpy(&hinstrument, hashstate->hinstrument,
3401 sizeof(HashInstrumentation));
3402
3403 /*
3404 * Merge results from workers. In the parallel-oblivious case, the
3405 * results from all participants should be identical, except where
3406 * participants didn't run the join at all so have no data. In the
3407 * parallel-aware case, we need to consider all the results. Each worker
3408 * may have seen a different subset of batches and we want to report the
3409 * highest memory usage across all batches. We take the maxima of other
3410 * values too, for the same reasons as in ExecHashAccumInstrumentation.
3411 */
3412 if (hashstate->shared_info)
3413 {
3414 SharedHashInfo *shared_info = hashstate->shared_info;
3415 int i;
3416
3417 for (i = 0; i < shared_info->num_workers; ++i)
3418 {
3419 HashInstrumentation *worker_hi = &shared_info->hinstrument[i];
3420
3421 hinstrument.nbuckets = Max(hinstrument.nbuckets,
3422 worker_hi->nbuckets);
3423 hinstrument.nbuckets_original = Max(hinstrument.nbuckets_original,
3424 worker_hi->nbuckets_original);
3425 hinstrument.nbatch = Max(hinstrument.nbatch,
3426 worker_hi->nbatch);
3427 hinstrument.nbatch_original = Max(hinstrument.nbatch_original,
3428 worker_hi->nbatch_original);
3429 hinstrument.space_peak = Max(hinstrument.space_peak,
3430 worker_hi->space_peak);
3431 }
3432 }
3433
3434 if (hinstrument.nbatch > 0)
3435 {
3436 uint64 spacePeakKb = BYTES_TO_KILOBYTES(hinstrument.space_peak);
3437
3438 if (es->format != EXPLAIN_FORMAT_TEXT)
3439 {
3440 ExplainPropertyInteger("Hash Buckets", NULL,
3441 hinstrument.nbuckets, es);
3442 ExplainPropertyInteger("Original Hash Buckets", NULL,
3443 hinstrument.nbuckets_original, es);
3444 ExplainPropertyInteger("Hash Batches", NULL,
3445 hinstrument.nbatch, es);
3446 ExplainPropertyInteger("Original Hash Batches", NULL,
3447 hinstrument.nbatch_original, es);
3448 ExplainPropertyUInteger("Peak Memory Usage", "kB",
3449 spacePeakKb, es);
3450 }
3451 else if (hinstrument.nbatch_original != hinstrument.nbatch ||
3452 hinstrument.nbuckets_original != hinstrument.nbuckets)
3453 {
3456 "Buckets: %d (originally %d) Batches: %d (originally %d) Memory Usage: " UINT64_FORMAT "kB\n",
3457 hinstrument.nbuckets,
3458 hinstrument.nbuckets_original,
3459 hinstrument.nbatch,
3460 hinstrument.nbatch_original,
3461 spacePeakKb);
3462 }
3463 else
3464 {
3467 "Buckets: %d Batches: %d Memory Usage: " UINT64_FORMAT "kB\n",
3468 hinstrument.nbuckets, hinstrument.nbatch,
3469 spacePeakKb);
3470 }
3471 }
3472}
#define Max(x, y)
Definition: c.h:969
uint64_t uint64
Definition: c.h:503
SharedHashInfo * shared_info
Definition: execnodes.h:2817
HashInstrumentation * hinstrument
Definition: execnodes.h:2824
HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2795

References appendStringInfo(), BYTES_TO_KILOBYTES, EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyInteger(), ExplainPropertyUInteger(), ExplainState::format, SharedHashInfo::hinstrument, HashState::hinstrument, i, Max, HashInstrumentation::nbatch, HashInstrumentation::nbatch_original, HashInstrumentation::nbuckets, HashInstrumentation::nbuckets_original, SharedHashInfo::num_workers, HashState::shared_info, HashInstrumentation::space_peak, ExplainState::str, and UINT64_FORMAT.

Referenced by ExplainNode().

◆ show_hashagg_info()

static void show_hashagg_info ( AggState aggstate,
ExplainState es 
)
static

Definition at line 3728 of file explain.c.

3729{
3730 Agg *agg = (Agg *) aggstate->ss.ps.plan;
3731 int64 memPeakKb = BYTES_TO_KILOBYTES(aggstate->hash_mem_peak);
3732
3733 if (agg->aggstrategy != AGG_HASHED &&
3734 agg->aggstrategy != AGG_MIXED)
3735 return;
3736
3737 if (es->format != EXPLAIN_FORMAT_TEXT)
3738 {
3739 if (es->costs)
3740 ExplainPropertyInteger("Planned Partitions", NULL,
3741 aggstate->hash_planned_partitions, es);
3742
3743 /*
3744 * During parallel query the leader may have not helped out. We
3745 * detect this by checking how much memory it used. If we find it
3746 * didn't do any work then we don't show its properties.
3747 */
3748 if (es->analyze && aggstate->hash_mem_peak > 0)
3749 {
3750 ExplainPropertyInteger("HashAgg Batches", NULL,
3751 aggstate->hash_batches_used, es);
3752 ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
3753 ExplainPropertyInteger("Disk Usage", "kB",
3754 aggstate->hash_disk_used, es);
3755 }
3756 }
3757 else
3758 {
3759 bool gotone = false;
3760
3761 if (es->costs && aggstate->hash_planned_partitions > 0)
3762 {
3764 appendStringInfo(es->str, "Planned Partitions: %d",
3765 aggstate->hash_planned_partitions);
3766 gotone = true;
3767 }
3768
3769 /*
3770 * During parallel query the leader may have not helped out. We
3771 * detect this by checking how much memory it used. If we find it
3772 * didn't do any work then we don't show its properties.
3773 */
3774 if (es->analyze && aggstate->hash_mem_peak > 0)
3775 {
3776 if (!gotone)
3778 else
3780
3781 appendStringInfo(es->str, "Batches: %d Memory Usage: " INT64_FORMAT "kB",
3782 aggstate->hash_batches_used, memPeakKb);
3783 gotone = true;
3784
3785 /* Only display disk usage if we spilled to disk */
3786 if (aggstate->hash_batches_used > 1)
3787 {
3788 appendStringInfo(es->str, " Disk Usage: " UINT64_FORMAT "kB",
3789 aggstate->hash_disk_used);
3790 }
3791 }
3792
3793 if (gotone)
3794 appendStringInfoChar(es->str, '\n');
3795 }
3796
3797 /* Display stats for each parallel worker */
3798 if (es->analyze && aggstate->shared_info != NULL)
3799 {
3800 for (int n = 0; n < aggstate->shared_info->num_workers; n++)
3801 {
3802 AggregateInstrumentation *sinstrument;
3803 uint64 hash_disk_used;
3804 int hash_batches_used;
3805
3806 sinstrument = &aggstate->shared_info->sinstrument[n];
3807 /* Skip workers that didn't do anything */
3808 if (sinstrument->hash_mem_peak == 0)
3809 continue;
3810 hash_disk_used = sinstrument->hash_disk_used;
3811 hash_batches_used = sinstrument->hash_batches_used;
3812 memPeakKb = BYTES_TO_KILOBYTES(sinstrument->hash_mem_peak);
3813
3814 if (es->workers_state)
3815 ExplainOpenWorker(n, es);
3816
3817 if (es->format == EXPLAIN_FORMAT_TEXT)
3818 {
3820
3821 appendStringInfo(es->str, "Batches: %d Memory Usage: " INT64_FORMAT "kB",
3822 hash_batches_used, memPeakKb);
3823
3824 /* Only display disk usage if we spilled to disk */
3825 if (hash_batches_used > 1)
3826 appendStringInfo(es->str, " Disk Usage: " UINT64_FORMAT "kB",
3827 hash_disk_used);
3828 appendStringInfoChar(es->str, '\n');
3829 }
3830 else
3831 {
3832 ExplainPropertyInteger("HashAgg Batches", NULL,
3833 hash_batches_used, es);
3834 ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
3835 es);
3836 ExplainPropertyInteger("Disk Usage", "kB", hash_disk_used, es);
3837 }
3838
3839 if (es->workers_state)
3840 ExplainCloseWorker(n, es);
3841 }
3842 }
3843}
#define INT64_FORMAT
Definition: c.h:520
void appendStringInfoSpaces(StringInfo str, int count)
Definition: stringinfo.c:260
uint64 hash_disk_used
Definition: execnodes.h:2581
int hash_planned_partitions
Definition: execnodes.h:2575
SharedAggInfo * shared_info
Definition: execnodes.h:2592
Size hash_mem_peak
Definition: execnodes.h:2578
int hash_batches_used
Definition: execnodes.h:2582
AggregateInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2497

References AGG_HASHED, AGG_MIXED, Agg::aggstrategy, ExplainState::analyze, appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), BYTES_TO_KILOBYTES, ExplainState::costs, EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyInteger(), ExplainState::format, AggregateInstrumentation::hash_batches_used, AggState::hash_batches_used, AggregateInstrumentation::hash_disk_used, AggState::hash_disk_used, AggregateInstrumentation::hash_mem_peak, AggState::hash_mem_peak, AggState::hash_planned_partitions, INT64_FORMAT, SharedAggInfo::num_workers, PlanState::plan, ScanState::ps, AggState::shared_info, SharedAggInfo::sinstrument, AggState::ss, ExplainState::str, UINT64_FORMAT, and ExplainState::workers_state.

Referenced by ExplainNode().

◆ show_incremental_sort_group_info()

static void show_incremental_sort_group_info ( IncrementalSortGroupInfo groupInfo,
const char *  groupLabel,
bool  indent,
ExplainState es 
)
static

Definition at line 3187 of file explain.c.

3189{
3190 ListCell *methodCell;
3191 List *methodNames = NIL;
3192
3193 /* Generate a list of sort methods used across all groups. */
3194 for (int bit = 0; bit < NUM_TUPLESORTMETHODS; bit++)
3195 {
3196 TuplesortMethod sortMethod = (1 << bit);
3197
3198 if (groupInfo->sortMethods & sortMethod)
3199 {
3200 const char *methodName = tuplesort_method_name(sortMethod);
3201
3202 methodNames = lappend(methodNames, unconstify(char *, methodName));
3203 }
3204 }
3205
3206 if (es->format == EXPLAIN_FORMAT_TEXT)
3207 {
3208 if (indent)
3209 appendStringInfoSpaces(es->str, es->indent * 2);
3210 appendStringInfo(es->str, "%s Groups: " INT64_FORMAT " Sort Method", groupLabel,
3211 groupInfo->groupCount);
3212 /* plural/singular based on methodNames size */
3213 if (list_length(methodNames) > 1)
3214 appendStringInfoString(es->str, "s: ");
3215 else
3216 appendStringInfoString(es->str, ": ");
3217 foreach(methodCell, methodNames)
3218 {
3219 appendStringInfoString(es->str, (char *) methodCell->ptr_value);
3220 if (foreach_current_index(methodCell) < list_length(methodNames) - 1)
3221 appendStringInfoString(es->str, ", ");
3222 }
3223
3224 if (groupInfo->maxMemorySpaceUsed > 0)
3225 {
3226 int64 avgSpace = groupInfo->totalMemorySpaceUsed / groupInfo->groupCount;
3227 const char *spaceTypeName;
3228
3230 appendStringInfo(es->str, " Average %s: " INT64_FORMAT "kB Peak %s: " INT64_FORMAT "kB",
3231 spaceTypeName, avgSpace,
3232 spaceTypeName, groupInfo->maxMemorySpaceUsed);
3233 }
3234
3235 if (groupInfo->maxDiskSpaceUsed > 0)
3236 {
3237 int64 avgSpace = groupInfo->totalDiskSpaceUsed / groupInfo->groupCount;
3238
3239 const char *spaceTypeName;
3240
3242 appendStringInfo(es->str, " Average %s: " INT64_FORMAT "kB Peak %s: " INT64_FORMAT "kB",
3243 spaceTypeName, avgSpace,
3244 spaceTypeName, groupInfo->maxDiskSpaceUsed);
3245 }
3246 }
3247 else
3248 {
3249 StringInfoData groupName;
3250
3251 initStringInfo(&groupName);
3252 appendStringInfo(&groupName, "%s Groups", groupLabel);
3253 ExplainOpenGroup("Incremental Sort Groups", groupName.data, true, es);
3254 ExplainPropertyInteger("Group Count", NULL, groupInfo->groupCount, es);
3255
3256 ExplainPropertyList("Sort Methods Used", methodNames, es);
3257
3258 if (groupInfo->maxMemorySpaceUsed > 0)
3259 {
3260 int64 avgSpace = groupInfo->totalMemorySpaceUsed / groupInfo->groupCount;
3261 const char *spaceTypeName;
3262 StringInfoData memoryName;
3263
3265 initStringInfo(&memoryName);
3266 appendStringInfo(&memoryName, "Sort Space %s", spaceTypeName);
3267 ExplainOpenGroup("Sort Space", memoryName.data, true, es);
3268
3269 ExplainPropertyInteger("Average Sort Space Used", "kB", avgSpace, es);
3270 ExplainPropertyInteger("Peak Sort Space Used", "kB",
3271 groupInfo->maxMemorySpaceUsed, es);
3272
3273 ExplainCloseGroup("Sort Space", memoryName.data, true, es);
3274 }
3275 if (groupInfo->maxDiskSpaceUsed > 0)
3276 {
3277 int64 avgSpace = groupInfo->totalDiskSpaceUsed / groupInfo->groupCount;
3278 const char *spaceTypeName;
3279 StringInfoData diskName;
3280
3282 initStringInfo(&diskName);
3283 appendStringInfo(&diskName, "Sort Space %s", spaceTypeName);
3284 ExplainOpenGroup("Sort Space", diskName.data, true, es);
3285
3286 ExplainPropertyInteger("Average Sort Space Used", "kB", avgSpace, es);
3287 ExplainPropertyInteger("Peak Sort Space Used", "kB",
3288 groupInfo->maxDiskSpaceUsed, es);
3289
3290 ExplainCloseGroup("Sort Space", diskName.data, true, es);
3291 }
3292
3293 ExplainCloseGroup("Incremental Sort Groups", groupName.data, true, es);
3294 }
3295}
#define unconstify(underlying_type, expr)
Definition: c.h:1216
void ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
const char * tuplesort_space_type_name(TuplesortSpaceType t)
Definition: tuplesort.c:2566
const char * tuplesort_method_name(TuplesortMethod m)
Definition: tuplesort.c:2543
#define NUM_TUPLESORTMETHODS
Definition: tuplesort.h:85
@ SORT_SPACE_TYPE_DISK
Definition: tuplesort.h:89
@ SORT_SPACE_TYPE_MEMORY
Definition: tuplesort.h:90
TuplesortMethod
Definition: tuplesort.h:77
void * ptr_value
Definition: pg_list.h:47
Datum bit(PG_FUNCTION_ARGS)
Definition: varbit.c:391

References appendStringInfo(), appendStringInfoSpaces(), appendStringInfoString(), bit(), StringInfoData::data, EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyInteger(), ExplainPropertyList(), foreach_current_index, ExplainState::format, IncrementalSortGroupInfo::groupCount, ExplainState::indent, initStringInfo(), INT64_FORMAT, lappend(), list_length(), IncrementalSortGroupInfo::maxDiskSpaceUsed, IncrementalSortGroupInfo::maxMemorySpaceUsed, NIL, NUM_TUPLESORTMETHODS, ListCell::ptr_value, SORT_SPACE_TYPE_DISK, SORT_SPACE_TYPE_MEMORY, IncrementalSortGroupInfo::sortMethods, ExplainState::str, IncrementalSortGroupInfo::totalDiskSpaceUsed, IncrementalSortGroupInfo::totalMemorySpaceUsed, tuplesort_method_name(), tuplesort_space_type_name(), and unconstify.

Referenced by show_incremental_sort_info().

◆ show_incremental_sort_info()

static void show_incremental_sort_info ( IncrementalSortState incrsortstate,
ExplainState es 
)
static

Definition at line 3301 of file explain.c.

3303{
3304 IncrementalSortGroupInfo *fullsortGroupInfo;
3305 IncrementalSortGroupInfo *prefixsortGroupInfo;
3306
3307 fullsortGroupInfo = &incrsortstate->incsort_info.fullsortGroupInfo;
3308
3309 if (!es->analyze)
3310 return;
3311
3312 /*
3313 * Since we never have any prefix groups unless we've first sorted a full
3314 * groups and transitioned modes (copying the tuples into a prefix group),
3315 * we don't need to do anything if there were 0 full groups.
3316 *
3317 * We still have to continue after this block if there are no full groups,
3318 * though, since it's possible that we have workers that did real work
3319 * even if the leader didn't participate.
3320 */
3321 if (fullsortGroupInfo->groupCount > 0)
3322 {
3323 show_incremental_sort_group_info(fullsortGroupInfo, "Full-sort", true, es);
3324 prefixsortGroupInfo = &incrsortstate->incsort_info.prefixsortGroupInfo;
3325 if (prefixsortGroupInfo->groupCount > 0)
3326 {
3327 if (es->format == EXPLAIN_FORMAT_TEXT)
3328 appendStringInfoChar(es->str, '\n');
3329 show_incremental_sort_group_info(prefixsortGroupInfo, "Pre-sorted", true, es);
3330 }
3331 if (es->format == EXPLAIN_FORMAT_TEXT)
3332 appendStringInfoChar(es->str, '\n');
3333 }
3334
3335 if (incrsortstate->shared_info != NULL)
3336 {
3337 int n;
3338 bool indent_first_line;
3339
3340 for (n = 0; n < incrsortstate->shared_info->num_workers; n++)
3341 {
3342 IncrementalSortInfo *incsort_info =
3343 &incrsortstate->shared_info->sinfo[n];
3344
3345 /*
3346 * If a worker hasn't processed any sort groups at all, then
3347 * exclude it from output since it either didn't launch or didn't
3348 * contribute anything meaningful.
3349 */
3350 fullsortGroupInfo = &incsort_info->fullsortGroupInfo;
3351
3352 /*
3353 * Since we never have any prefix groups unless we've first sorted
3354 * a full groups and transitioned modes (copying the tuples into a
3355 * prefix group), we don't need to do anything if there were 0
3356 * full groups.
3357 */
3358 if (fullsortGroupInfo->groupCount == 0)
3359 continue;
3360
3361 if (es->workers_state)
3362 ExplainOpenWorker(n, es);
3363
3364 indent_first_line = es->workers_state == NULL || es->verbose;
3365 show_incremental_sort_group_info(fullsortGroupInfo, "Full-sort",
3366 indent_first_line, es);
3367 prefixsortGroupInfo = &incsort_info->prefixsortGroupInfo;
3368 if (prefixsortGroupInfo->groupCount > 0)
3369 {
3370 if (es->format == EXPLAIN_FORMAT_TEXT)
3371 appendStringInfoChar(es->str, '\n');
3372 show_incremental_sort_group_info(prefixsortGroupInfo, "Pre-sorted", true, es);
3373 }
3374 if (es->format == EXPLAIN_FORMAT_TEXT)
3375 appendStringInfoChar(es->str, '\n');
3376
3377 if (es->workers_state)
3378 ExplainCloseWorker(n, es);
3379 }
3380 }
3381}
static void show_incremental_sort_group_info(IncrementalSortGroupInfo *groupInfo, const char *groupLabel, bool indent, ExplainState *es)
Definition: explain.c:3187
IncrementalSortGroupInfo prefixsortGroupInfo
Definition: execnodes.h:2420
IncrementalSortGroupInfo fullsortGroupInfo
Definition: execnodes.h:2419
SharedIncrementalSortInfo * shared_info
Definition: execnodes.h:2465
IncrementalSortInfo incsort_info
Definition: execnodes.h:2459
IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2430

References ExplainState::analyze, appendStringInfoChar(), EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainOpenWorker(), ExplainState::format, IncrementalSortInfo::fullsortGroupInfo, IncrementalSortGroupInfo::groupCount, IncrementalSortState::incsort_info, SharedIncrementalSortInfo::num_workers, IncrementalSortInfo::prefixsortGroupInfo, IncrementalSortState::shared_info, show_incremental_sort_group_info(), SharedIncrementalSortInfo::sinfo, ExplainState::str, ExplainState::verbose, and ExplainState::workers_state.

Referenced by ExplainNode().

◆ show_incremental_sort_keys()

static void show_incremental_sort_keys ( IncrementalSortState incrsortstate,
List ancestors,
ExplainState es 
)
static

Definition at line 2595 of file explain.c.

2597{
2598 IncrementalSort *plan = (IncrementalSort *) incrsortstate->ss.ps.plan;
2599
2600 show_sort_group_keys((PlanState *) incrsortstate, "Sort Key",
2601 plan->sort.numCols, plan->nPresortedCols,
2602 plan->sort.sortColIdx,
2603 plan->sort.sortOperators, plan->sort.collations,
2604 plan->sort.nullsFirst,
2605 ancestors, es);
2606}

References PlanState::plan, plan, ScanState::ps, show_sort_group_keys(), and IncrementalSortState::ss.

Referenced by ExplainNode().

◆ show_indexsearches_info()

static void show_indexsearches_info ( PlanState planstate,
ExplainState es 
)
static

Definition at line 3850 of file explain.c.

3851{
3852 Plan *plan = planstate->plan;
3853 SharedIndexScanInstrumentation *SharedInfo = NULL;
3854 uint64 nsearches = 0;
3855
3856 if (!es->analyze)
3857 return;
3858
3859 /* Initialize counters with stats from the local process first */
3860 switch (nodeTag(plan))
3861 {
3862 case T_IndexScan:
3863 {
3864 IndexScanState *indexstate = ((IndexScanState *) planstate);
3865
3866 nsearches = indexstate->iss_Instrument.nsearches;
3867 SharedInfo = indexstate->iss_SharedInfo;
3868 break;
3869 }
3870 case T_IndexOnlyScan:
3871 {
3872 IndexOnlyScanState *indexstate = ((IndexOnlyScanState *) planstate);
3873
3874 nsearches = indexstate->ioss_Instrument.nsearches;
3875 SharedInfo = indexstate->ioss_SharedInfo;
3876 break;
3877 }
3878 case T_BitmapIndexScan:
3879 {
3880 BitmapIndexScanState *indexstate = ((BitmapIndexScanState *) planstate);
3881
3882 nsearches = indexstate->biss_Instrument.nsearches;
3883 SharedInfo = indexstate->biss_SharedInfo;
3884 break;
3885 }
3886 default:
3887 break;
3888 }
3889
3890 /* Next get the sum of the counters set within each and every process */
3891 if (SharedInfo)
3892 {
3893 for (int i = 0; i < SharedInfo->num_workers; ++i)
3894 {
3895 IndexScanInstrumentation *winstrument = &SharedInfo->winstrument[i];
3896
3897 nsearches += winstrument->nsearches;
3898 }
3899 }
3900
3901 ExplainPropertyUInteger("Index Searches", NULL, nsearches, es);
3902}
SharedIndexScanInstrumentation * biss_SharedInfo
Definition: execnodes.h:1804
IndexScanInstrumentation biss_Instrument
Definition: execnodes.h:1803
SharedIndexScanInstrumentation * ioss_SharedInfo
Definition: execnodes.h:1763
IndexScanInstrumentation ioss_Instrument
Definition: execnodes.h:1762
IndexScanInstrumentation iss_Instrument
Definition: execnodes.h:1711
SharedIndexScanInstrumentation * iss_SharedInfo
Definition: execnodes.h:1712
IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: genam.h:47

References ExplainState::analyze, BitmapIndexScanState::biss_Instrument, BitmapIndexScanState::biss_SharedInfo, ExplainPropertyUInteger(), i, IndexOnlyScanState::ioss_Instrument, IndexOnlyScanState::ioss_SharedInfo, IndexScanState::iss_Instrument, IndexScanState::iss_SharedInfo, nodeTag, IndexScanInstrumentation::nsearches, SharedIndexScanInstrumentation::num_workers, PlanState::plan, plan, and SharedIndexScanInstrumentation::winstrument.

Referenced by ExplainNode().

◆ show_instrumentation_count()

static void show_instrumentation_count ( const char *  qlabel,
int  which,
PlanState planstate,
ExplainState es 
)
static

Definition at line 3977 of file explain.c.

3979{
3980 double nfiltered;
3981 double nloops;
3982
3983 if (!es->analyze || !planstate->instrument)
3984 return;
3985
3986 if (which == 2)
3987 nfiltered = planstate->instrument->nfiltered2;
3988 else
3989 nfiltered = planstate->instrument->nfiltered1;
3990 nloops = planstate->instrument->nloops;
3991
3992 /* In text mode, suppress zero counts; they're not interesting enough */
3993 if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
3994 {
3995 if (nloops > 0)
3996 ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es);
3997 else
3998 ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es);
3999 }
4000}
double nfiltered2
Definition: instrument.h:91
double nfiltered1
Definition: instrument.h:90

References ExplainState::analyze, EXPLAIN_FORMAT_TEXT, ExplainPropertyFloat(), ExplainState::format, PlanState::instrument, Instrumentation::nfiltered1, Instrumentation::nfiltered2, and Instrumentation::nloops.

Referenced by ExplainNode(), and show_modifytable_info().

◆ show_material_info()

static void show_material_info ( MaterialState mstate,
ExplainState es 
)
static

Definition at line 3479 of file explain.c.

3480{
3481 char *maxStorageType;
3482 int64 maxSpaceUsed;
3483
3484 Tuplestorestate *tupstore = mstate->tuplestorestate;
3485
3486 /*
3487 * Nothing to show if ANALYZE option wasn't used or if execution didn't
3488 * get as far as creating the tuplestore.
3489 */
3490 if (!es->analyze || tupstore == NULL)
3491 return;
3492
3493 tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
3494 show_storage_info(maxStorageType, maxSpaceUsed, es);
3495}
Tuplestorestate * tuplestorestate
Definition: execnodes.h:2287

References ExplainState::analyze, show_storage_info(), tuplestore_get_stats(), and MaterialState::tuplestorestate.

Referenced by ExplainNode().

◆ show_memoize_info()

static void show_memoize_info ( MemoizeState mstate,
List ancestors,
ExplainState es 
)
static

Definition at line 3594 of file explain.c.

3595{
3596 Plan *plan = ((PlanState *) mstate)->plan;
3597 ListCell *lc;
3598 List *context;
3599 StringInfoData keystr;
3600 char *separator = "";
3601 bool useprefix;
3602 int64 memPeakKb;
3603
3604 initStringInfo(&keystr);
3605
3606 /*
3607 * It's hard to imagine having a memoize node with fewer than 2 RTEs, but
3608 * let's just keep the same useprefix logic as elsewhere in this file.
3609 */
3610 useprefix = es->rtable_size > 1 || es->verbose;
3611
3612 /* Set up deparsing context */
3614 plan,
3615 ancestors);
3616
3617 foreach(lc, ((Memoize *) plan)->param_exprs)
3618 {
3619 Node *expr = (Node *) lfirst(lc);
3620
3622
3623 appendStringInfoString(&keystr, deparse_expression(expr, context,
3624 useprefix, false));
3625 separator = ", ";
3626 }
3627
3628 ExplainPropertyText("Cache Key", keystr.data, es);
3629 ExplainPropertyText("Cache Mode", mstate->binary_mode ? "binary" : "logical", es);
3630
3631 pfree(keystr.data);
3632
3633 if (!es->analyze)
3634 return;
3635
3636 if (mstate->stats.cache_misses > 0)
3637 {
3638 /*
3639 * mem_peak is only set when we freed memory, so we must use mem_used
3640 * when mem_peak is 0.
3641 */
3642 if (mstate->stats.mem_peak > 0)
3643 memPeakKb = BYTES_TO_KILOBYTES(mstate->stats.mem_peak);
3644 else
3645 memPeakKb = BYTES_TO_KILOBYTES(mstate->mem_used);
3646
3647 if (es->format != EXPLAIN_FORMAT_TEXT)
3648 {
3649 ExplainPropertyInteger("Cache Hits", NULL, mstate->stats.cache_hits, es);
3650 ExplainPropertyInteger("Cache Misses", NULL, mstate->stats.cache_misses, es);
3651 ExplainPropertyInteger("Cache Evictions", NULL, mstate->stats.cache_evictions, es);
3652 ExplainPropertyInteger("Cache Overflows", NULL, mstate->stats.cache_overflows, es);
3653 ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
3654 }
3655 else
3656 {
3659 "Hits: " UINT64_FORMAT " Misses: " UINT64_FORMAT " Evictions: " UINT64_FORMAT " Overflows: " UINT64_FORMAT " Memory Usage: " INT64_FORMAT "kB\n",
3660 mstate->stats.cache_hits,
3661 mstate->stats.cache_misses,
3662 mstate->stats.cache_evictions,
3663 mstate->stats.cache_overflows,
3664 memPeakKb);
3665 }
3666 }
3667
3668 if (mstate->shared_info == NULL)
3669 return;
3670
3671 /* Show details from parallel workers */
3672 for (int n = 0; n < mstate->shared_info->num_workers; n++)
3673 {
3675
3676 si = &mstate->shared_info->sinstrument[n];
3677
3678 /*
3679 * Skip workers that didn't do any work. We needn't bother checking
3680 * for cache hits as a miss will always occur before a cache hit.
3681 */
3682 if (si->cache_misses == 0)
3683 continue;
3684
3685 if (es->workers_state)
3686 ExplainOpenWorker(n, es);
3687
3688 /*
3689 * Since the worker's MemoizeState.mem_used field is unavailable to
3690 * us, ExecEndMemoize will have set the
3691 * MemoizeInstrumentation.mem_peak field for us. No need to do the
3692 * zero checks like we did for the serial case above.
3693 */
3694 memPeakKb = BYTES_TO_KILOBYTES(si->mem_peak);
3695
3696 if (es->format == EXPLAIN_FORMAT_TEXT)
3697 {
3700 "Hits: " UINT64_FORMAT " Misses: " UINT64_FORMAT " Evictions: " UINT64_FORMAT " Overflows: " UINT64_FORMAT " Memory Usage: " INT64_FORMAT "kB\n",
3701 si->cache_hits, si->cache_misses,
3703 memPeakKb);
3704 }
3705 else
3706 {
3707 ExplainPropertyInteger("Cache Hits", NULL,
3708 si->cache_hits, es);
3709 ExplainPropertyInteger("Cache Misses", NULL,
3710 si->cache_misses, es);
3711 ExplainPropertyInteger("Cache Evictions", NULL,
3712 si->cache_evictions, es);
3713 ExplainPropertyInteger("Cache Overflows", NULL,
3714 si->cache_overflows, es);
3715 ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
3716 es);
3717 }
3718
3719 if (es->workers_state)
3720 ExplainCloseWorker(n, es);
3721 }
3722}
uint64 mem_used
Definition: execnodes.h:2340
SharedMemoizeInfo * shared_info
Definition: execnodes.h:2355
MemoizeInstrumentation stats
Definition: execnodes.h:2354
bool binary_mode
Definition: execnodes.h:2352
MemoizeInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2316

References ExplainState::analyze, appendStringInfo(), appendStringInfoString(), MemoizeState::binary_mode, BYTES_TO_KILOBYTES, MemoizeInstrumentation::cache_evictions, MemoizeInstrumentation::cache_hits, MemoizeInstrumentation::cache_misses, MemoizeInstrumentation::cache_overflows, config_generic::context, StringInfoData::data, ExplainState::deparse_cxt, deparse_expression(), EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyInteger(), ExplainPropertyText(), ExplainState::format, initStringInfo(), INT64_FORMAT, lfirst, MemoizeInstrumentation::mem_peak, MemoizeState::mem_used, SharedMemoizeInfo::num_workers, pfree(), plan, ExplainState::rtable_size, set_deparse_context_plan(), MemoizeState::shared_info, SharedMemoizeInfo::sinstrument, MemoizeState::stats, ExplainState::str, UINT64_FORMAT, ExplainState::verbose, and ExplainState::workers_state.

Referenced by ExplainNode().

◆ show_memory_counters()

static void show_memory_counters ( ExplainState es,
const MemoryContextCounters mem_counters 
)
static

Definition at line 4310 of file explain.c.

4311{
4312 int64 memUsedkB = BYTES_TO_KILOBYTES(mem_counters->totalspace -
4313 mem_counters->freespace);
4314 int64 memAllocatedkB = BYTES_TO_KILOBYTES(mem_counters->totalspace);
4315
4316 if (es->format == EXPLAIN_FORMAT_TEXT)
4317 {
4320 "Memory: used=" INT64_FORMAT "kB allocated=" INT64_FORMAT "kB",
4321 memUsedkB, memAllocatedkB);
4322 appendStringInfoChar(es->str, '\n');
4323 }
4324 else
4325 {
4326 ExplainPropertyInteger("Memory Used", "kB", memUsedkB, es);
4327 ExplainPropertyInteger("Memory Allocated", "kB", memAllocatedkB, es);
4328 }
4329}

References appendStringInfo(), appendStringInfoChar(), BYTES_TO_KILOBYTES, EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyInteger(), ExplainState::format, MemoryContextCounters::freespace, INT64_FORMAT, ExplainState::str, and MemoryContextCounters::totalspace.

Referenced by ExplainOnePlan().

◆ show_merge_append_keys()

static void show_merge_append_keys ( MergeAppendState mstate,
List ancestors,
ExplainState es 
)
static

Definition at line 2612 of file explain.c.

2614{
2615 MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
2616
2617 show_sort_group_keys((PlanState *) mstate, "Sort Key",
2618 plan->numCols, 0, plan->sortColIdx,
2619 plan->sortOperators, plan->collations,
2620 plan->nullsFirst,
2621 ancestors, es);
2622}
PlanState ps
Definition: execnodes.h:1528

References PlanState::plan, plan, MergeAppendState::ps, and show_sort_group_keys().

Referenced by ExplainNode().

◆ show_modifytable_info()

static void show_modifytable_info ( ModifyTableState mtstate,
List ancestors,
ExplainState es 
)
static

Definition at line 4532 of file explain.c.

4534{
4535 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
4536 const char *operation;
4537 const char *foperation;
4538 bool labeltargets;
4539 int j;
4540 List *idxNames = NIL;
4541 ListCell *lst;
4542
4543 switch (node->operation)
4544 {
4545 case CMD_INSERT:
4546 operation = "Insert";
4547 foperation = "Foreign Insert";
4548 break;
4549 case CMD_UPDATE:
4550 operation = "Update";
4551 foperation = "Foreign Update";
4552 break;
4553 case CMD_DELETE:
4554 operation = "Delete";
4555 foperation = "Foreign Delete";
4556 break;
4557 case CMD_MERGE:
4558 operation = "Merge";
4559 /* XXX unsupported for now, but avoid compiler noise */
4560 foperation = "Foreign Merge";
4561 break;
4562 default:
4563 operation = "???";
4564 foperation = "Foreign ???";
4565 break;
4566 }
4567
4568 /*
4569 * Should we explicitly label target relations?
4570 *
4571 * If there's only one target relation, do not list it if it's the
4572 * relation named in the query, or if it has been pruned. (Normally
4573 * mtstate->resultRelInfo doesn't include pruned relations, but a single
4574 * pruned target relation may be present, if all other target relations
4575 * have been pruned. See ExecInitModifyTable().)
4576 */
4577 labeltargets = (mtstate->mt_nrels > 1 ||
4578 (mtstate->mt_nrels == 1 &&
4579 mtstate->resultRelInfo[0].ri_RangeTableIndex != node->nominalRelation &&
4581 mtstate->ps.state->es_unpruned_relids)));
4582
4583 if (labeltargets)
4584 ExplainOpenGroup("Target Tables", "Target Tables", false, es);
4585
4586 for (j = 0; j < mtstate->mt_nrels; j++)
4587 {
4588 ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
4589 FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
4590
4591 if (labeltargets)
4592 {
4593 /* Open a group for this target */
4594 ExplainOpenGroup("Target Table", NULL, true, es);
4595
4596 /*
4597 * In text mode, decorate each target with operation type, so that
4598 * ExplainTargetRel's output of " on foo" will read nicely.
4599 */
4600 if (es->format == EXPLAIN_FORMAT_TEXT)
4601 {
4604 fdwroutine ? foperation : operation);
4605 }
4606
4607 /* Identify target */
4608 ExplainTargetRel((Plan *) node,
4609 resultRelInfo->ri_RangeTableIndex,
4610 es);
4611
4612 if (es->format == EXPLAIN_FORMAT_TEXT)
4613 {
4614 appendStringInfoChar(es->str, '\n');
4615 es->indent++;
4616 }
4617 }
4618
4619 /* Give FDW a chance if needed */
4620 if (!resultRelInfo->ri_usesFdwDirectModify &&
4621 fdwroutine != NULL &&
4622 fdwroutine->ExplainForeignModify != NULL)
4623 {
4624 List *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
4625
4626 fdwroutine->ExplainForeignModify(mtstate,
4627 resultRelInfo,
4628 fdw_private,
4629 j,
4630 es);
4631 }
4632
4633 if (labeltargets)
4634 {
4635 /* Undo the indentation we added in text format */
4636 if (es->format == EXPLAIN_FORMAT_TEXT)
4637 es->indent--;
4638
4639 /* Close the group */
4640 ExplainCloseGroup("Target Table", NULL, true, es);
4641 }
4642 }
4643
4644 /* Gather names of ON CONFLICT arbiter indexes */
4645 foreach(lst, node->arbiterIndexes)
4646 {
4647 char *indexname = get_rel_name(lfirst_oid(lst));
4648
4649 idxNames = lappend(idxNames, indexname);
4650 }
4651
4652 if (node->onConflictAction != ONCONFLICT_NONE)
4653 {
4654 ExplainPropertyText("Conflict Resolution",
4656 "NOTHING" : "UPDATE",
4657 es);
4658
4659 /*
4660 * Don't display arbiter indexes at all when DO NOTHING variant
4661 * implicitly ignores all conflicts
4662 */
4663 if (idxNames)
4664 ExplainPropertyList("Conflict Arbiter Indexes", idxNames, es);
4665
4666 /* ON CONFLICT DO UPDATE WHERE qual is specially displayed */
4667 if (node->onConflictWhere)
4668 {
4669 show_upper_qual((List *) node->onConflictWhere, "Conflict Filter",
4670 &mtstate->ps, ancestors, es);
4671 show_instrumentation_count("Rows Removed by Conflict Filter", 1, &mtstate->ps, es);
4672 }
4673
4674 /* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
4675 if (es->analyze && mtstate->ps.instrument)
4676 {
4677 double total;
4678 double insert_path;
4679 double other_path;
4680
4681 InstrEndLoop(outerPlanState(mtstate)->instrument);
4682
4683 /* count the number of source rows */
4684 total = outerPlanState(mtstate)->instrument->ntuples;
4685 other_path = mtstate->ps.instrument->ntuples2;
4686 insert_path = total - other_path;
4687
4688 ExplainPropertyFloat("Tuples Inserted", NULL,
4689 insert_path, 0, es);
4690 ExplainPropertyFloat("Conflicting Tuples", NULL,
4691 other_path, 0, es);
4692 }
4693 }
4694 else if (node->operation == CMD_MERGE)
4695 {
4696 /* EXPLAIN ANALYZE display of tuples processed */
4697 if (es->analyze && mtstate->ps.instrument)
4698 {
4699 double total;
4700 double insert_path;
4701 double update_path;
4702 double delete_path;
4703 double skipped_path;
4704
4705 InstrEndLoop(outerPlanState(mtstate)->instrument);
4706
4707 /* count the number of source rows */
4708 total = outerPlanState(mtstate)->instrument->ntuples;
4709 insert_path = mtstate->mt_merge_inserted;
4710 update_path = mtstate->mt_merge_updated;
4711 delete_path = mtstate->mt_merge_deleted;
4712 skipped_path = total - insert_path - update_path - delete_path;
4713 Assert(skipped_path >= 0);
4714
4715 if (es->format == EXPLAIN_FORMAT_TEXT)
4716 {
4717 if (total > 0)
4718 {
4720 appendStringInfoString(es->str, "Tuples:");
4721 if (insert_path > 0)
4722 appendStringInfo(es->str, " inserted=%.0f", insert_path);
4723 if (update_path > 0)
4724 appendStringInfo(es->str, " updated=%.0f", update_path);
4725 if (delete_path > 0)
4726 appendStringInfo(es->str, " deleted=%.0f", delete_path);
4727 if (skipped_path > 0)
4728 appendStringInfo(es->str, " skipped=%.0f", skipped_path);
4729 appendStringInfoChar(es->str, '\n');
4730 }
4731 }
4732 else
4733 {
4734 ExplainPropertyFloat("Tuples Inserted", NULL, insert_path, 0, es);
4735 ExplainPropertyFloat("Tuples Updated", NULL, update_path, 0, es);
4736 ExplainPropertyFloat("Tuples Deleted", NULL, delete_path, 0, es);
4737 ExplainPropertyFloat("Tuples Skipped", NULL, skipped_path, 0, es);
4738 }
4739 }
4740 }
4741
4742 if (labeltargets)
4743 ExplainCloseGroup("Target Tables", "Target Tables", false, es);
4744}
@ ONCONFLICT_NONE
Definition: nodes.h:420
@ ONCONFLICT_NOTHING
Definition: nodes.h:421
#define lfirst_oid(lc)
Definition: pg_list.h:174
Bitmapset * es_unpruned_relids
Definition: execnodes.h:665
ExplainForeignModify_function ExplainForeignModify
Definition: fdwapi.h:253
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1396
double mt_merge_deleted
Definition: execnodes.h:1451
double mt_merge_inserted
Definition: execnodes.h:1449
double mt_merge_updated
Definition: execnodes.h:1450
PlanState ps
Definition: execnodes.h:1391
Index nominalRelation
Definition: plannodes.h:293
List * arbiterIndexes
Definition: plannodes.h:321
CmdType operation
Definition: plannodes.h:289
List * fdwPrivLists
Definition: plannodes.h:311
Node * onConflictWhere
Definition: plannodes.h:327
OnConflictAction onConflictAction
Definition: plannodes.h:319
Index ri_RangeTableIndex
Definition: execnodes.h:472
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:528
bool ri_usesFdwDirectModify
Definition: execnodes.h:534

References ExplainState::analyze, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), ModifyTable::arbiterIndexes, Assert(), bms_is_member(), CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, EState::es_unpruned_relids, EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), FdwRoutine::ExplainForeignModify, ExplainIndentText(), ExplainOpenGroup(), ExplainPropertyFloat(), ExplainPropertyList(), ExplainPropertyText(), ExplainTargetRel(), ModifyTable::fdwPrivLists, ExplainState::format, get_rel_name(), ExplainState::indent, InstrEndLoop(), PlanState::instrument, j, lappend(), lfirst_oid, list_nth(), ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_inserted, ModifyTableState::mt_merge_updated, ModifyTableState::mt_nrels, NIL, ModifyTable::nominalRelation, Instrumentation::ntuples2, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ModifyTable::onConflictAction, ModifyTable::onConflictWhere, ModifyTable::operation, outerPlanState, PlanState::plan, ModifyTableState::ps, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_usesFdwDirectModify, show_instrumentation_count(), show_upper_qual(), PlanState::state, and ExplainState::str.

Referenced by ExplainNode().

◆ show_plan_tlist()

static void show_plan_tlist ( PlanState planstate,
List ancestors,
ExplainState es 
)
static

Definition at line 2450 of file explain.c.

2451{
2452 Plan *plan = planstate->plan;
2453 List *context;
2454 List *result = NIL;
2455 bool useprefix;
2456 ListCell *lc;
2457
2458 /* No work if empty tlist (this occurs eg in bitmap indexscans) */
2459 if (plan->targetlist == NIL)
2460 return;
2461 /* The tlist of an Append isn't real helpful, so suppress it */
2462 if (IsA(plan, Append))
2463 return;
2464 /* Likewise for MergeAppend and RecursiveUnion */
2465 if (IsA(plan, MergeAppend))
2466 return;
2467 if (IsA(plan, RecursiveUnion))
2468 return;
2469
2470 /*
2471 * Likewise for ForeignScan that executes a direct INSERT/UPDATE/DELETE
2472 *
2473 * Note: the tlist for a ForeignScan that executes a direct INSERT/UPDATE
2474 * might contain subplan output expressions that are confusing in this
2475 * context. The tlist for a ForeignScan that executes a direct UPDATE/
2476 * DELETE always contains "junk" target columns to identify the exact row
2477 * to update or delete, which would be confusing in this context. So, we
2478 * suppress it in all the cases.
2479 */
2480 if (IsA(plan, ForeignScan) &&
2481 ((ForeignScan *) plan)->operation != CMD_SELECT)
2482 return;
2483
2484 /* Set up deparsing context */
2486 plan,
2487 ancestors);
2488 useprefix = es->rtable_size > 1;
2489
2490 /* Deparse each result column (we now include resjunk ones) */
2491 foreach(lc, plan->targetlist)
2492 {
2493 TargetEntry *tle = (TargetEntry *) lfirst(lc);
2494
2495 result = lappend(result,
2496 deparse_expression((Node *) tle->expr, context,
2497 useprefix, false));
2498 }
2499
2500 /* Print results */
2501 ExplainPropertyList("Output", result, es);
2502}

References CMD_SELECT, config_generic::context, ExplainState::deparse_cxt, deparse_expression(), ExplainPropertyList(), TargetEntry::expr, IsA, lappend(), lfirst, NIL, PlanState::plan, plan, ExplainState::rtable_size, and set_deparse_context_plan().

Referenced by ExplainNode().

◆ show_qual()

static void show_qual ( List qual,
const char *  qlabel,
PlanState planstate,
List ancestors,
bool  useprefix,
ExplainState es 
)
static

Definition at line 2531 of file explain.c.

2534{
2535 Node *node;
2536
2537 /* No work if empty qual */
2538 if (qual == NIL)
2539 return;
2540
2541 /* Convert AND list to explicit AND */
2542 node = (Node *) make_ands_explicit(qual);
2543
2544 /* And show it */
2545 show_expression(node, qlabel, planstate, ancestors, useprefix, es);
2546}
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:799

References make_ands_explicit(), NIL, and show_expression().

Referenced by show_scan_qual(), and show_upper_qual().

◆ show_recursive_union_info()

static void show_recursive_union_info ( RecursiveUnionState rstate,
ExplainState es 
)
static

Definition at line 3563 of file explain.c.

3564{
3565 char *maxStorageType,
3566 *tempStorageType;
3567 int64 maxSpaceUsed,
3568 tempSpaceUsed;
3569
3570 if (!es->analyze)
3571 return;
3572
3573 /*
3574 * Recursive union node uses two tuplestores. We employ the storage type
3575 * from one of them which consumed more memory/disk than the other. The
3576 * storage size is sum of the two.
3577 */
3578 tuplestore_get_stats(rstate->working_table, &tempStorageType,
3579 &tempSpaceUsed);
3580 tuplestore_get_stats(rstate->intermediate_table, &maxStorageType,
3581 &maxSpaceUsed);
3582
3583 if (tempSpaceUsed > maxSpaceUsed)
3584 maxStorageType = tempStorageType;
3585
3586 maxSpaceUsed += tempSpaceUsed;
3587 show_storage_info(maxStorageType, maxSpaceUsed, es);
3588}
Tuplestorestate * working_table
Definition: execnodes.h:1556
Tuplestorestate * intermediate_table
Definition: execnodes.h:1557

References ExplainState::analyze, RecursiveUnionState::intermediate_table, show_storage_info(), tuplestore_get_stats(), and RecursiveUnionState::working_table.

Referenced by ExplainNode().

◆ show_scan_qual()

static void show_scan_qual ( List qual,
const char *  qlabel,
PlanState planstate,
List ancestors,
ExplainState es 
)
static

Definition at line 2552 of file explain.c.

2555{
2556 bool useprefix;
2557
2558 useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
2559 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2560}
static void show_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
Definition: explain.c:2531

References IsA, PlanState::plan, show_qual(), and ExplainState::verbose.

Referenced by ExplainNode().

◆ show_sort_group_keys()

static void show_sort_group_keys ( PlanState planstate,
const char *  qlabel,
int  nkeys,
int  nPresortedKeys,
AttrNumber keycols,
Oid sortOperators,
Oid collations,
bool *  nullsFirst,
List ancestors,
ExplainState es 
)
static

Definition at line 2780 of file explain.c.

2784{
2785 Plan *plan = planstate->plan;
2786 List *context;
2787 List *result = NIL;
2788 List *resultPresorted = NIL;
2789 StringInfoData sortkeybuf;
2790 bool useprefix;
2791 int keyno;
2792
2793 if (nkeys <= 0)
2794 return;
2795
2796 initStringInfo(&sortkeybuf);
2797
2798 /* Set up deparsing context */
2800 plan,
2801 ancestors);
2802 useprefix = (es->rtable_size > 1 || es->verbose);
2803
2804 for (keyno = 0; keyno < nkeys; keyno++)
2805 {
2806 /* find key expression in tlist */
2807 AttrNumber keyresno = keycols[keyno];
2808 TargetEntry *target = get_tle_by_resno(plan->targetlist,
2809 keyresno);
2810 char *exprstr;
2811
2812 if (!target)
2813 elog(ERROR, "no tlist entry for key %d", keyresno);
2814 /* Deparse the expression, showing any top-level cast */
2815 exprstr = deparse_expression((Node *) target->expr, context,
2816 useprefix, true);
2817 resetStringInfo(&sortkeybuf);
2818 appendStringInfoString(&sortkeybuf, exprstr);
2819 /* Append sort order information, if relevant */
2820 if (sortOperators != NULL)
2821 show_sortorder_options(&sortkeybuf,
2822 (Node *) target->expr,
2823 sortOperators[keyno],
2824 collations[keyno],
2825 nullsFirst[keyno]);
2826 /* Emit one property-list item per sort key */
2827 result = lappend(result, pstrdup(sortkeybuf.data));
2828 if (keyno < nPresortedKeys)
2829 resultPresorted = lappend(resultPresorted, exprstr);
2830 }
2831
2832 ExplainPropertyList(qlabel, result, es);
2833 if (nPresortedKeys > 0)
2834 ExplainPropertyList("Presorted Key", resultPresorted, es);
2835}
static void show_sortorder_options(StringInfo buf, Node *sortexpr, Oid sortOperator, Oid collation, bool nullsFirst)
Definition: explain.c:2842
char * pstrdup(const char *in)
Definition: mcxt.c:1699
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:126

References appendStringInfoString(), config_generic::context, StringInfoData::data, ExplainState::deparse_cxt, deparse_expression(), elog, ERROR, ExplainPropertyList(), TargetEntry::expr, get_tle_by_resno(), initStringInfo(), lappend(), NIL, PlanState::plan, plan, pstrdup(), resetStringInfo(), ExplainState::rtable_size, set_deparse_context_plan(), show_sortorder_options(), and ExplainState::verbose.

Referenced by show_agg_keys(), show_group_keys(), show_grouping_set_keys(), show_incremental_sort_keys(), show_merge_append_keys(), and show_sort_keys().

◆ show_sort_info()

static void show_sort_info ( SortState sortstate,
ExplainState es 
)
static

Definition at line 3096 of file explain.c.

3097{
3098 if (!es->analyze)
3099 return;
3100
3101 if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
3102 {
3105 const char *sortMethod;
3106 const char *spaceType;
3107 int64 spaceUsed;
3108
3109 tuplesort_get_stats(state, &stats);
3110 sortMethod = tuplesort_method_name(stats.sortMethod);
3111 spaceType = tuplesort_space_type_name(stats.spaceType);
3112 spaceUsed = stats.spaceUsed;
3113
3114 if (es->format == EXPLAIN_FORMAT_TEXT)
3115 {
3117 appendStringInfo(es->str, "Sort Method: %s %s: " INT64_FORMAT "kB\n",
3118 sortMethod, spaceType, spaceUsed);
3119 }
3120 else
3121 {
3122 ExplainPropertyText("Sort Method", sortMethod, es);
3123 ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
3124 ExplainPropertyText("Sort Space Type", spaceType, es);
3125 }
3126 }
3127
3128 /*
3129 * You might think we should just skip this stanza entirely when
3130 * es->hide_workers is true, but then we'd get no sort-method output at
3131 * all. We have to make it look like worker 0's data is top-level data.
3132 * This is easily done by just skipping the OpenWorker/CloseWorker calls.
3133 * Currently, we don't worry about the possibility that there are multiple
3134 * workers in such a case; if there are, duplicate output fields will be
3135 * emitted.
3136 */
3137 if (sortstate->shared_info != NULL)
3138 {
3139 int n;
3140
3141 for (n = 0; n < sortstate->shared_info->num_workers; n++)
3142 {
3143 TuplesortInstrumentation *sinstrument;
3144 const char *sortMethod;
3145 const char *spaceType;
3146 int64 spaceUsed;
3147
3148 sinstrument = &sortstate->shared_info->sinstrument[n];
3149 if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS)
3150 continue; /* ignore any unfilled slots */
3151 sortMethod = tuplesort_method_name(sinstrument->sortMethod);
3152 spaceType = tuplesort_space_type_name(sinstrument->spaceType);
3153 spaceUsed = sinstrument->spaceUsed;
3154
3155 if (es->workers_state)
3156 ExplainOpenWorker(n, es);
3157
3158 if (es->format == EXPLAIN_FORMAT_TEXT)
3159 {
3162 "Sort Method: %s %s: " INT64_FORMAT "kB\n",
3163 sortMethod, spaceType, spaceUsed);
3164 }
3165 else
3166 {
3167 ExplainPropertyText("Sort Method", sortMethod, es);
3168 ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
3169 ExplainPropertyText("Sort Space Type", spaceType, es);
3170 }
3171
3172 if (es->workers_state)
3173 ExplainCloseWorker(n, es);
3174 }
3175 }
3176}
TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2381
bool sort_Done
Definition: execnodes.h:2394
void * tuplesortstate
Definition: execnodes.h:2397
SharedSortInfo * shared_info
Definition: execnodes.h:2400
TuplesortMethod sortMethod
Definition: tuplesort.h:113
TuplesortSpaceType spaceType
Definition: tuplesort.h:114
Definition: regguts.h:323
void tuplesort_get_stats(Tuplesortstate *state, TuplesortInstrumentation *stats)
Definition: tuplesort.c:2499
@ SORT_TYPE_STILL_IN_PROGRESS
Definition: tuplesort.h:78

References ExplainState::analyze, appendStringInfo(), EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyInteger(), ExplainPropertyText(), ExplainState::format, INT64_FORMAT, SharedSortInfo::num_workers, SortState::shared_info, SharedSortInfo::sinstrument, SortState::sort_Done, SORT_TYPE_STILL_IN_PROGRESS, TuplesortInstrumentation::sortMethod, TuplesortInstrumentation::spaceType, TuplesortInstrumentation::spaceUsed, ExplainState::str, tuplesort_get_stats(), tuplesort_method_name(), tuplesort_space_type_name(), SortState::tuplesortstate, and ExplainState::workers_state.

Referenced by ExplainNode().

◆ show_sort_keys()

static void show_sort_keys ( SortState sortstate,
List ancestors,
ExplainState es 
)
static

Definition at line 2580 of file explain.c.

2581{
2582 Sort *plan = (Sort *) sortstate->ss.ps.plan;
2583
2584 show_sort_group_keys((PlanState *) sortstate, "Sort Key",
2585 plan->numCols, 0, plan->sortColIdx,
2586 plan->sortOperators, plan->collations,
2587 plan->nullsFirst,
2588 ancestors, es);
2589}
ScanState ss
Definition: execnodes.h:2390

References PlanState::plan, plan, ScanState::ps, show_sort_group_keys(), and SortState::ss.

Referenced by ExplainNode().

◆ show_sortorder_options()

static void show_sortorder_options ( StringInfo  buf,
Node sortexpr,
Oid  sortOperator,
Oid  collation,
bool  nullsFirst 
)
static

Definition at line 2842 of file explain.c.

2844{
2845 Oid sortcoltype = exprType(sortexpr);
2846 bool reverse = false;
2847 TypeCacheEntry *typentry;
2848
2849 typentry = lookup_type_cache(sortcoltype,
2851
2852 /*
2853 * Print COLLATE if it's not default for the column's type. There are
2854 * some cases where this is redundant, eg if expression is a column whose
2855 * declared collation is that collation, but it's hard to distinguish that
2856 * here (and arguably, printing COLLATE explicitly is a good idea anyway
2857 * in such cases).
2858 */
2859 if (OidIsValid(collation) && collation != get_typcollation(sortcoltype))
2860 {
2861 char *collname = get_collation_name(collation);
2862
2863 if (collname == NULL)
2864 elog(ERROR, "cache lookup failed for collation %u", collation);
2865 appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
2866 }
2867
2868 /* Print direction if not ASC, or USING if non-default sort operator */
2869 if (sortOperator == typentry->gt_opr)
2870 {
2871 appendStringInfoString(buf, " DESC");
2872 reverse = true;
2873 }
2874 else if (sortOperator != typentry->lt_opr)
2875 {
2876 char *opname = get_opname(sortOperator);
2877
2878 if (opname == NULL)
2879 elog(ERROR, "cache lookup failed for operator %u", sortOperator);
2880 appendStringInfo(buf, " USING %s", opname);
2881 /* Determine whether operator would be considered ASC or DESC */
2882 (void) get_equality_op_for_ordering_op(sortOperator, &reverse);
2883 }
2884
2885 /* Add NULLS FIRST/LAST only if it wouldn't be default */
2886 if (nullsFirst && !reverse)
2887 {
2888 appendStringInfoString(buf, " NULLS FIRST");
2889 }
2890 else if (!nullsFirst && reverse)
2891 {
2892 appendStringInfoString(buf, " NULLS LAST");
2893 }
2894}
char * get_opname(Oid opno)
Definition: lsyscache.c:1393
Oid get_equality_op_for_ordering_op(Oid opno, bool *reverse)
Definition: lsyscache.c:290
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3139
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1070
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
static char * buf
Definition: pg_test_fsync.c:72
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:386
#define TYPECACHE_GT_OPR
Definition: typcache.h:139
#define TYPECACHE_LT_OPR
Definition: typcache.h:138

References appendStringInfo(), appendStringInfoString(), buf, elog, ERROR, exprType(), get_collation_name(), get_equality_op_for_ordering_op(), get_opname(), get_typcollation(), TypeCacheEntry::gt_opr, lookup_type_cache(), TypeCacheEntry::lt_opr, OidIsValid, quote_identifier(), TYPECACHE_GT_OPR, and TYPECACHE_LT_OPR.

Referenced by show_sort_group_keys().

◆ show_storage_info()

static void show_storage_info ( char *  maxStorageType,
int64  maxSpaceUsed,
ExplainState es 
)
static

Definition at line 3007 of file explain.c.

3008{
3009 int64 maxSpaceUsedKB = BYTES_TO_KILOBYTES(maxSpaceUsed);
3010
3011 if (es->format != EXPLAIN_FORMAT_TEXT)
3012 {
3013 ExplainPropertyText("Storage", maxStorageType, es);
3014 ExplainPropertyInteger("Maximum Storage", "kB", maxSpaceUsedKB, es);
3015 }
3016 else
3017 {
3020 "Storage: %s Maximum Storage: " INT64_FORMAT "kB\n",
3021 maxStorageType,
3022 maxSpaceUsedKB);
3023 }
3024}

References appendStringInfo(), BYTES_TO_KILOBYTES, EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyInteger(), ExplainPropertyText(), ExplainState::format, INT64_FORMAT, and ExplainState::str.

Referenced by show_ctescan_info(), show_material_info(), show_recursive_union_info(), show_table_func_scan_info(), and show_windowagg_info().

◆ show_table_func_scan_info()

static void show_table_func_scan_info ( TableFuncScanState tscanstate,
ExplainState es 
)
static

Definition at line 3544 of file explain.c.

3545{
3546 char *maxStorageType;
3547 int64 maxSpaceUsed;
3548
3549 Tuplestorestate *tupstore = tscanstate->tupstore;
3550
3551 if (!es->analyze || tupstore == NULL)
3552 return;
3553
3554 tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
3555 show_storage_info(maxStorageType, maxSpaceUsed, es);
3556}
Tuplestorestate * tupstore
Definition: execnodes.h:2033

References ExplainState::analyze, show_storage_info(), tuplestore_get_stats(), and TableFuncScanState::tupstore.

Referenced by ExplainNode().

◆ show_tablesample()

static void show_tablesample ( TableSampleClause tsc,
PlanState planstate,
List ancestors,
ExplainState es 
)
static

Definition at line 3030 of file explain.c.

3032{
3033 List *context;
3034 bool useprefix;
3035 char *method_name;
3036 List *params = NIL;
3037 char *repeatable;
3038 ListCell *lc;
3039
3040 /* Set up deparsing context */
3042 planstate->plan,
3043 ancestors);
3044 useprefix = es->rtable_size > 1;
3045
3046 /* Get the tablesample method name */
3047 method_name = get_func_name(tsc->tsmhandler);
3048
3049 /* Deparse parameter expressions */
3050 foreach(lc, tsc->args)
3051 {
3052 Node *arg = (Node *) lfirst(lc);
3053
3054 params = lappend(params,
3055 deparse_expression(arg, context,
3056 useprefix, false));
3057 }
3058 if (tsc->repeatable)
3059 repeatable = deparse_expression((Node *) tsc->repeatable, context,
3060 useprefix, false);
3061 else
3062 repeatable = NULL;
3063
3064 /* Print results */
3065 if (es->format == EXPLAIN_FORMAT_TEXT)
3066 {
3067 bool first = true;
3068
3070 appendStringInfo(es->str, "Sampling: %s (", method_name);
3071 foreach(lc, params)
3072 {
3073 if (!first)
3074 appendStringInfoString(es->str, ", ");
3075 appendStringInfoString(es->str, (const char *) lfirst(lc));
3076 first = false;
3077 }
3078 appendStringInfoChar(es->str, ')');
3079 if (repeatable)
3080 appendStringInfo(es->str, " REPEATABLE (%s)", repeatable);
3081 appendStringInfoChar(es->str, '\n');
3082 }
3083 else
3084 {
3085 ExplainPropertyText("Sampling Method", method_name, es);
3086 ExplainPropertyList("Sampling Parameters", params, es);
3087 if (repeatable)
3088 ExplainPropertyText("Repeatable Seed", repeatable, es);
3089 }
3090}
void * arg

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, TableSampleClause::args, config_generic::context, ExplainState::deparse_cxt, deparse_expression(), EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyList(), ExplainPropertyText(), ExplainState::format, get_func_name(), lappend(), lfirst, NIL, PlanState::plan, TableSampleClause::repeatable, ExplainState::rtable_size, set_deparse_context_plan(), ExplainState::str, and TableSampleClause::tsmhandler.

Referenced by ExplainNode().

◆ show_tidbitmap_info()

static void show_tidbitmap_info ( BitmapHeapScanState planstate,
ExplainState es 
)
static

Definition at line 3908 of file explain.c.

3909{
3910 if (!es->analyze)
3911 return;
3912
3913 if (es->format != EXPLAIN_FORMAT_TEXT)
3914 {
3915 ExplainPropertyUInteger("Exact Heap Blocks", NULL,
3916 planstate->stats.exact_pages, es);
3917 ExplainPropertyUInteger("Lossy Heap Blocks", NULL,
3918 planstate->stats.lossy_pages, es);
3919 }
3920 else
3921 {
3922 if (planstate->stats.exact_pages > 0 || planstate->stats.lossy_pages > 0)
3923 {
3925 appendStringInfoString(es->str, "Heap Blocks:");
3926 if (planstate->stats.exact_pages > 0)
3927 appendStringInfo(es->str, " exact=" UINT64_FORMAT, planstate->stats.exact_pages);
3928 if (planstate->stats.lossy_pages > 0)
3929 appendStringInfo(es->str, " lossy=" UINT64_FORMAT, planstate->stats.lossy_pages);
3930 appendStringInfoChar(es->str, '\n');
3931 }
3932 }
3933
3934 /* Display stats for each parallel worker */
3935 if (planstate->pstate != NULL)
3936 {
3937 for (int n = 0; n < planstate->sinstrument->num_workers; n++)
3938 {
3940
3941 if (si->exact_pages == 0 && si->lossy_pages == 0)
3942 continue;
3943
3944 if (es->workers_state)
3945 ExplainOpenWorker(n, es);
3946
3947 if (es->format == EXPLAIN_FORMAT_TEXT)
3948 {
3950 appendStringInfoString(es->str, "Heap Blocks:");
3951 if (si->exact_pages > 0)
3952 appendStringInfo(es->str, " exact=" UINT64_FORMAT, si->exact_pages);
3953 if (si->lossy_pages > 0)
3954 appendStringInfo(es->str, " lossy=" UINT64_FORMAT, si->lossy_pages);
3955 appendStringInfoChar(es->str, '\n');
3956 }
3957 else
3958 {
3959 ExplainPropertyUInteger("Exact Heap Blocks", NULL,
3960 si->exact_pages, es);
3961 ExplainPropertyUInteger("Lossy Heap Blocks", NULL,
3962 si->lossy_pages, es);
3963 }
3964
3965 if (es->workers_state)
3966 ExplainCloseWorker(n, es);
3967 }
3968 }
3969}
ParallelBitmapHeapState * pstate
Definition: execnodes.h:1889
BitmapHeapScanInstrumentation stats
Definition: execnodes.h:1887
SharedBitmapHeapInstrumentation * sinstrument
Definition: execnodes.h:1890
BitmapHeapScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:1867

References ExplainState::analyze, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), BitmapHeapScanInstrumentation::exact_pages, EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyUInteger(), ExplainState::format, BitmapHeapScanInstrumentation::lossy_pages, SharedBitmapHeapInstrumentation::num_workers, BitmapHeapScanState::pstate, SharedBitmapHeapInstrumentation::sinstrument, BitmapHeapScanState::sinstrument, BitmapHeapScanState::stats, ExplainState::str, UINT64_FORMAT, and ExplainState::workers_state.

Referenced by ExplainNode().

◆ show_upper_qual()

static void show_upper_qual ( List qual,
const char *  qlabel,
PlanState planstate,
List ancestors,
ExplainState es 
)
static

Definition at line 2566 of file explain.c.

2569{
2570 bool useprefix;
2571
2572 useprefix = (es->rtable_size > 1 || es->verbose);
2573 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2574}

References ExplainState::rtable_size, show_qual(), and ExplainState::verbose.

Referenced by ExplainNode(), and show_modifytable_info().

◆ show_wal_usage()

static void show_wal_usage ( ExplainState es,
const WalUsage usage 
)
static

Definition at line 4267 of file explain.c.

4268{
4269 if (es->format == EXPLAIN_FORMAT_TEXT)
4270 {
4271 /* Show only positive counter values. */
4272 if ((usage->wal_records > 0) || (usage->wal_fpi > 0) ||
4273 (usage->wal_bytes > 0) || (usage->wal_buffers_full > 0))
4274 {
4276 appendStringInfoString(es->str, "WAL:");
4277
4278 if (usage->wal_records > 0)
4279 appendStringInfo(es->str, " records=%lld",
4280 (long long) usage->wal_records);
4281 if (usage->wal_fpi > 0)
4282 appendStringInfo(es->str, " fpi=%lld",
4283 (long long) usage->wal_fpi);
4284 if (usage->wal_bytes > 0)
4285 appendStringInfo(es->str, " bytes=" UINT64_FORMAT,
4286 usage->wal_bytes);
4287 if (usage->wal_buffers_full > 0)
4288 appendStringInfo(es->str, " buffers full=%lld",
4289 (long long) usage->wal_buffers_full);
4290 appendStringInfoChar(es->str, '\n');
4291 }
4292 }
4293 else
4294 {
4295 ExplainPropertyInteger("WAL Records", NULL,
4296 usage->wal_records, es);
4297 ExplainPropertyInteger("WAL FPI", NULL,
4298 usage->wal_fpi, es);
4299 ExplainPropertyUInteger("WAL Bytes", NULL,
4300 usage->wal_bytes, es);
4301 ExplainPropertyInteger("WAL Buffers Full", NULL,
4302 usage->wal_buffers_full, es);
4303 }
4304}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyInteger(), ExplainPropertyUInteger(), ExplainState::format, ExplainState::str, UINT64_FORMAT, and usage().

Referenced by ExplainNode().

◆ show_window_def()

static void show_window_def ( WindowAggState planstate,
List ancestors,
ExplainState es 
)
static

Definition at line 2900 of file explain.c.

2901{
2902 WindowAgg *wagg = (WindowAgg *) planstate->ss.ps.plan;
2903 StringInfoData wbuf;
2904 bool needspace = false;
2905
2906 initStringInfo(&wbuf);
2907 appendStringInfo(&wbuf, "%s AS (", quote_identifier(wagg->winname));
2908
2909 /* The key columns refer to the tlist of the child plan */
2910 ancestors = lcons(wagg, ancestors);
2911 if (wagg->partNumCols > 0)
2912 {
2913 appendStringInfoString(&wbuf, "PARTITION BY ");
2914 show_window_keys(&wbuf, outerPlanState(planstate),
2915 wagg->partNumCols, wagg->partColIdx,
2916 ancestors, es);
2917 needspace = true;
2918 }
2919 if (wagg->ordNumCols > 0)
2920 {
2921 if (needspace)
2922 appendStringInfoChar(&wbuf, ' ');
2923 appendStringInfoString(&wbuf, "ORDER BY ");
2924 show_window_keys(&wbuf, outerPlanState(planstate),
2925 wagg->ordNumCols, wagg->ordColIdx,
2926 ancestors, es);
2927 needspace = true;
2928 }
2929 ancestors = list_delete_first(ancestors);
2931 {
2932 List *context;
2933 bool useprefix;
2934 char *framestr;
2935
2936 /* Set up deparsing context for possible frame expressions */
2938 (Plan *) wagg,
2939 ancestors);
2940 useprefix = (es->rtable_size > 1 || es->verbose);
2942 wagg->startOffset,
2943 wagg->endOffset,
2944 context,
2945 useprefix);
2946 if (needspace)
2947 appendStringInfoChar(&wbuf, ' ');
2948 appendStringInfoString(&wbuf, framestr);
2949 pfree(framestr);
2950 }
2951 appendStringInfoChar(&wbuf, ')');
2952 ExplainPropertyText("Window", wbuf.data, es);
2953 pfree(wbuf.data);
2954}
static void show_window_keys(StringInfo buf, PlanState *planstate, int nkeys, AttrNumber *keycols, List *ancestors, ExplainState *es)
Definition: explain.c:2963
#define FRAMEOPTION_NONDEFAULT
Definition: parsenodes.h:592
char * get_window_frame_options_for_explain(int frameOptions, Node *startOffset, Node *endOffset, List *dpcontext, bool forceprefix)
Definition: ruleutils.c:6897
ScanState ss
Definition: execnodes.h:2617
char * winname
Definition: plannodes.h:1182
int partNumCols
Definition: plannodes.h:1188
Node * endOffset
Definition: plannodes.h:1218
Node * startOffset
Definition: plannodes.h:1215
int ordNumCols
Definition: plannodes.h:1200
int frameOptions
Definition: plannodes.h:1212

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), config_generic::context, ExplainState::deparse_cxt, ExplainPropertyText(), FRAMEOPTION_NONDEFAULT, WindowAgg::frameOptions, get_window_frame_options_for_explain(), initStringInfo(), lcons(), list_delete_first(), WindowAgg::ordNumCols, outerPlanState, WindowAgg::partNumCols, pfree(), PlanState::plan, ScanState::ps, quote_identifier(), ExplainState::rtable_size, set_deparse_context_plan(), show_window_keys(), WindowAggState::ss, ExplainState::verbose, and WindowAgg::winname.

Referenced by ExplainNode().

◆ show_window_keys()

static void show_window_keys ( StringInfo  buf,
PlanState planstate,
int  nkeys,
AttrNumber keycols,
List ancestors,
ExplainState es 
)
static

Definition at line 2963 of file explain.c.

2966{
2967 Plan *plan = planstate->plan;
2968 List *context;
2969 bool useprefix;
2970
2971 /* Set up deparsing context */
2973 plan,
2974 ancestors);
2975 useprefix = (es->rtable_size > 1 || es->verbose);
2976
2977 for (int keyno = 0; keyno < nkeys; keyno++)
2978 {
2979 /* find key expression in tlist */
2980 AttrNumber keyresno = keycols[keyno];
2981 TargetEntry *target = get_tle_by_resno(plan->targetlist,
2982 keyresno);
2983 char *exprstr;
2984
2985 if (!target)
2986 elog(ERROR, "no tlist entry for key %d", keyresno);
2987 /* Deparse the expression, showing any top-level cast */
2988 exprstr = deparse_expression((Node *) target->expr, context,
2989 useprefix, true);
2990 if (keyno > 0)
2992 appendStringInfoString(buf, exprstr);
2993 pfree(exprstr);
2994
2995 /*
2996 * We don't attempt to provide sort order information because
2997 * WindowAgg carries equality operators not comparison operators;
2998 * compare show_agg_keys.
2999 */
3000 }
3001}

References appendStringInfoString(), buf, config_generic::context, ExplainState::deparse_cxt, deparse_expression(), elog, ERROR, TargetEntry::expr, get_tle_by_resno(), pfree(), PlanState::plan, plan, ExplainState::rtable_size, set_deparse_context_plan(), and ExplainState::verbose.

Referenced by show_window_def().

◆ show_windowagg_info()

static void show_windowagg_info ( WindowAggState winstate,
ExplainState es 
)
static

Definition at line 3502 of file explain.c.

3503{
3504 char *maxStorageType;
3505 int64 maxSpaceUsed;
3506
3507 Tuplestorestate *tupstore = winstate->buffer;
3508
3509 /*
3510 * Nothing to show if ANALYZE option wasn't used or if execution didn't
3511 * get as far as creating the tuplestore.
3512 */
3513 if (!es->analyze || tupstore == NULL)
3514 return;
3515
3516 tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
3517 show_storage_info(maxStorageType, maxSpaceUsed, es);
3518}
Tuplestorestate * buffer
Definition: execnodes.h:2628

References ExplainState::analyze, WindowAggState::buffer, show_storage_info(), and tuplestore_get_stats().

Referenced by ExplainNode().

◆ standard_ExplainOneQuery()

void standard_ExplainOneQuery ( Query query,
int  cursorOptions,
IntoClause into,
ExplainState es,
const char *  queryString,
ParamListInfo  params,
QueryEnvironment queryEnv 
)

Definition at line 318 of file explain.c.

322{
324 instr_time planstart,
325 planduration;
326 BufferUsage bufusage_start,
327 bufusage;
328 MemoryContextCounters mem_counters;
329 MemoryContext planner_ctx = NULL;
330 MemoryContext saved_ctx = NULL;
331
332 if (es->memory)
333 {
334 /*
335 * Create a new memory context to measure planner's memory consumption
336 * accurately. Note that if the planner were to be modified to use a
337 * different memory context type, here we would be changing that to
338 * AllocSet, which might be undesirable. However, we don't have a way
339 * to create a context of the same type as another, so we pray and
340 * hope that this is OK.
341 */
343 "explain analyze planner context",
345 saved_ctx = MemoryContextSwitchTo(planner_ctx);
346 }
347
348 if (es->buffers)
349 bufusage_start = pgBufferUsage;
350 INSTR_TIME_SET_CURRENT(planstart);
351
352 /* plan the query */
353 plan = pg_plan_query(query, queryString, cursorOptions, params);
354
355 INSTR_TIME_SET_CURRENT(planduration);
356 INSTR_TIME_SUBTRACT(planduration, planstart);
357
358 if (es->memory)
359 {
360 MemoryContextSwitchTo(saved_ctx);
361 MemoryContextMemConsumed(planner_ctx, &mem_counters);
362 }
363
364 /* calc differences of buffer counters. */
365 if (es->buffers)
366 {
367 memset(&bufusage, 0, sizeof(BufferUsage));
368 BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
369 }
370
371 /* run it (if needed) and produce output */
372 ExplainOnePlan(plan, NULL, NULL, -1, into, es, queryString, params,
373 queryEnv,
374 &planduration, (es->buffers ? &bufusage : NULL),
375 es->memory ? &mem_counters : NULL);
376}
void ExplainOnePlan(PlannedStmt *plannedstmt, CachedPlan *cplan, CachedPlanSource *plansource, int query_index, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv, const instr_time *planduration, const BufferUsage *bufusage, const MemoryContextCounters *mem_counters)
Definition: explain.c:495
BufferUsage pgBufferUsage
Definition: instrument.c:20
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:248
void MemoryContextMemConsumed(MemoryContext context, MemoryContextCounters *consumed)
Definition: mcxt.c:786
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
PlannedStmt * pg_plan_query(Query *querytree, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:882

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, ExplainState::buffers, BufferUsageAccumDiff(), CurrentMemoryContext, ExplainOnePlan(), INSTR_TIME_SET_CURRENT, INSTR_TIME_SUBTRACT, ExplainState::memory, MemoryContextMemConsumed(), MemoryContextSwitchTo(), pg_plan_query(), pgBufferUsage, and plan.

Referenced by ExplainOneQuery().

Variable Documentation

◆ explain_get_index_name_hook

explain_get_index_name_hook_type explain_get_index_name_hook = NULL

Definition at line 53 of file explain.c.

Referenced by explain_get_index_name().

◆ explain_per_node_hook

explain_per_node_hook_type explain_per_node_hook = NULL

Definition at line 57 of file explain.c.

Referenced by ExplainNode().

◆ explain_per_plan_hook

explain_per_plan_hook_type explain_per_plan_hook = NULL

Definition at line 56 of file explain.c.

Referenced by ExplainOnePlan().

◆ ExplainOneQuery_hook

ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL

Definition at line 50 of file explain.c.

Referenced by ExplainOneQuery().