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 show_result_replacement_info (Result *result, ExplainState *es)
 
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, 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 1166 of file explain.c.

1167{
1168 instr_time endtime;
1169
1170 INSTR_TIME_SET_CURRENT(endtime);
1171 INSTR_TIME_SUBTRACT(endtime, *starttime);
1172 return INSTR_TIME_GET_DOUBLE(endtime);
1173}
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:122
#define INSTR_TIME_GET_DOUBLE(t)
Definition: instr_time.h:190
#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 4049 of file explain.c.

4050{
4051 const char *result;
4052
4054 result = (*explain_get_index_name_hook) (indexId);
4055 else
4056 result = NULL;
4057 if (result == NULL)
4058 {
4059 /* default behavior: look it up in the catalogs */
4060 result = get_rel_name(indexId);
4061 if (result == NULL)
4062 elog(ERROR, "cache lookup failed for index %u", indexId);
4063 }
4064 return result;
4065}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
explain_get_index_name_hook_type explain_get_index_name_hook
Definition: explain.c:53
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2078

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 5060 of file explain.c.

5061{
5062 ExplainWorkersState *wstate = es->workers_state;
5063
5064 Assert(wstate);
5065 Assert(n >= 0 && n < wstate->num_workers);
5066 Assert(wstate->worker_inited[n]);
5067
5068 /*
5069 * Save formatting state in case we do another ExplainOpenWorker(), then
5070 * pop the formatting stack.
5071 */
5072 ExplainSaveGroup(es, 2, &wstate->worker_state_save[n]);
5073
5074 /*
5075 * In TEXT format, if we didn't actually produce any output line(s) then
5076 * truncate off the partial line emitted by ExplainOpenWorker. (This is
5077 * to avoid bogus output if, say, show_buffer_usage chooses not to print
5078 * anything for the worker.) Also fix up the indent level.
5079 */
5080 if (es->format == EXPLAIN_FORMAT_TEXT)
5081 {
5082 while (es->str->len > 0 && es->str->data[es->str->len - 1] != '\n')
5083 es->str->data[--(es->str->len)] = '\0';
5084
5085 es->indent--;
5086 }
5087
5088 /* Restore prior output buffer pointer */
5089 es->str = wstate->prev_str;
5090}
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 4981 of file explain.c.

4982{
4983 ExplainWorkersState *wstate;
4984
4986 wstate->num_workers = num_workers;
4987 wstate->worker_inited = (bool *) palloc0(num_workers * sizeof(bool));
4988 wstate->worker_str = (StringInfoData *)
4989 palloc0(num_workers * sizeof(StringInfoData));
4990 wstate->worker_state_save = (int *) palloc(num_workers * sizeof(int));
4991 return wstate;
4992}
#define palloc_object(type)
Definition: fe_memutils.h:74
void * palloc0(Size size)
Definition: mcxt.c:1417
void * palloc(Size size)
Definition: mcxt.c:1387
StringInfoData * worker_str
Definition: explain_state.h:39

References ExplainWorkersState::num_workers, palloc(), palloc0(), palloc_object, 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 4959 of file explain.c.

4960{
4961 ListCell *cell;
4962 const char *label =
4963 (list_length(css->custom_ps) != 1 ? "children" : "child");
4964
4965 foreach(cell, css->custom_ps)
4966 ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
4967}
static void ExplainNode(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
Definition: explain.c:1358
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:2142

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

Referenced by ExplainNode().

◆ ExplainFlushWorkersState()

static void ExplainFlushWorkersState ( ExplainState es)
static

Definition at line 5096 of file explain.c.

5097{
5098 ExplainWorkersState *wstate = es->workers_state;
5099
5100 ExplainOpenGroup("Workers", "Workers", false, es);
5101 for (int i = 0; i < wstate->num_workers; i++)
5102 {
5103 if (wstate->worker_inited[i])
5104 {
5105 /* This must match previous ExplainOpenSetAsideGroup call */
5106 ExplainOpenGroup("Worker", NULL, true, es);
5108 ExplainCloseGroup("Worker", NULL, true, es);
5109
5110 pfree(wstate->worker_str[i].data);
5111 }
5112 }
5113 ExplainCloseGroup("Workers", "Workers", false, es);
5114
5115 pfree(wstate->worker_inited);
5116 pfree(wstate->worker_str);
5117 pfree(wstate->worker_state_save);
5118 pfree(wstate);
5119}
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:77
void pfree(void *pointer)
Definition: mcxt.c:1616
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 4357 of file explain.c.

4359{
4360 const char *indexname = explain_get_index_name(indexid);
4361
4362 if (es->format == EXPLAIN_FORMAT_TEXT)
4363 {
4364 if (ScanDirectionIsBackward(indexorderdir))
4365 appendStringInfoString(es->str, " Backward");
4366 appendStringInfo(es->str, " using %s", quote_identifier(indexname));
4367 }
4368 else
4369 {
4370 const char *scandir;
4371
4372 switch (indexorderdir)
4373 {
4375 scandir = "Backward";
4376 break;
4378 scandir = "Forward";
4379 break;
4380 default:
4381 scandir = "???";
4382 break;
4383 }
4384 ExplainPropertyText("Scan Direction", scandir, es);
4385 ExplainPropertyText("Index Name", indexname, es);
4386 }
4387}
static const char * explain_get_index_name(Oid indexId)
Definition: explain.c:4049
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13062
#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 4871 of file explain.c.

4873{
4874 int j;
4875
4876 for (j = 0; j < nplans; j++)
4877 ExplainNode(planstates[j], ancestors,
4878 "Member", NULL, es);
4879}
int j
Definition: isn.c:78

References ExplainNode(), and j.

Referenced by ExplainNode().

◆ ExplainMissingMembers()

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

Definition at line 4889 of file explain.c.

4890{
4891 if (nplans < nchildren || es->format != EXPLAIN_FORMAT_TEXT)
4892 ExplainPropertyInteger("Subplans Removed", NULL,
4893 nchildren - nplans, es);
4894}
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 4406 of file explain.c.

4407{
4408 ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
4409}
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
Definition: explain.c:4415
#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 1358 of file explain.c.

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

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, INSTR_TIME_GET_MILLISEC, 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_result_replacement_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,
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.

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

References ExplainState::analyze, appendStringInfoString(), Assert(), ExplainState::buffers, CMD_UTILITY, CommandCounterIncrement(), PlannedStmt::commandType, ExplainState::costs, CreateExplainSerializeDestReceiver(), CreateIntoRelDestReceiver(), CreateQueryDesc(), generate_unaccent_rules::dest, elapsed_time(), EXEC_FLAG_EXPLAIN_GENERIC, EXEC_FLAG_EXPLAIN_ONLY, ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), 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(), 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 294 of file explain.c.

297{
298 /* planner will not cope with utility statements */
299 if (query->commandType == CMD_UTILITY)
300 {
301 ExplainOneUtility(query->utilityStmt, into, es, pstate, params);
302 return;
303 }
304
305 /* if an advisor plugin is present, let it manage things */
307 (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
308 pstate->p_sourcetext, params, pstate->p_queryEnv);
309 else
310 standard_ExplainOneQuery(query, cursorOptions, into, es,
311 pstate->p_sourcetext, params, pstate->p_queryEnv);
312}
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:319
QueryEnvironment * p_queryEnv
Definition: parse_node.h:223
const char * p_sourcetext
Definition: parse_node.h:195
CmdType commandType
Definition: parsenodes.h:121
Node * utilityStmt
Definition: parsenodes.h:141

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:571
bool CreateTableAsRelExists(CreateTableAsStmt *ctas)
Definition: createas.c:393
static void ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
Definition: explain.c:294
void ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
#define copyObject(obj)
Definition: nodes.h:232
@ OBJECT_MATVIEW
Definition: parsenodes.h:2375
@ OBJECT_TABLE
Definition: parsenodes.h:2393
#define CURSOR_OPT_PARALLEL_OK
Definition: parsenodes.h:3425
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:68
#define linitial_node(type, l)
Definition: pg_list.h:181
static bool IsQueryIdEnabled(void)
Definition: queryjumble.h:104
JumbleState * JumbleQuery(Query *query)
List * QueryRewrite(Query *parsetree)
IntoClause * into
Definition: parsenodes.h:4057
ObjectType objtype
Definition: parsenodes.h:4058

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 4998 of file explain.c.

4999{
5000 ExplainWorkersState *wstate = es->workers_state;
5001
5002 Assert(wstate);
5003 Assert(n >= 0 && n < wstate->num_workers);
5004
5005 /* Save prior output buffer pointer */
5006 wstate->prev_str = es->str;
5007
5008 if (!wstate->worker_inited[n])
5009 {
5010 /* First time through, so create the buffer for this worker */
5011 initStringInfo(&wstate->worker_str[n]);
5012 es->str = &wstate->worker_str[n];
5013
5014 /*
5015 * Push suitable initial formatting state for this worker's field
5016 * group. We allow one extra logical nesting level, since this group
5017 * will eventually be wrapped in an outer "Workers" group.
5018 */
5019 ExplainOpenSetAsideGroup("Worker", NULL, true, 2, es);
5020
5021 /*
5022 * In non-TEXT formats we always emit a "Worker Number" field, even if
5023 * there's no other data for this worker.
5024 */
5025 if (es->format != EXPLAIN_FORMAT_TEXT)
5026 ExplainPropertyInteger("Worker Number", NULL, n, es);
5027
5028 wstate->worker_inited[n] = true;
5029 }
5030 else
5031 {
5032 /* Resuming output for a worker we've already emitted some data for */
5033 es->str = &wstate->worker_str[n];
5034
5035 /* Restore formatting state saved by last ExplainCloseWorker() */
5036 ExplainRestoreGroup(es, 2, &wstate->worker_state_save[n]);
5037 }
5038
5039 /*
5040 * In TEXT format, prefix the first output line for this worker with
5041 * "Worker N:". Then, any additional lines should be indented one more
5042 * stop than the "Worker N" line is.
5043 */
5044 if (es->format == EXPLAIN_FORMAT_TEXT)
5045 {
5046 if (es->str->len == 0)
5047 {
5049 appendStringInfo(es->str, "Worker %d: ", n);
5050 }
5051
5052 es->indent++;
5053 }
5054}
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 1185 of file explain.c.

1186{
1187 Plan *plan = planstate->plan;
1188
1189 switch (nodeTag(plan))
1190 {
1191 case T_SeqScan:
1192 case T_SampleScan:
1193 case T_IndexScan:
1194 case T_IndexOnlyScan:
1195 case T_BitmapHeapScan:
1196 case T_TidScan:
1197 case T_TidRangeScan:
1198 case T_SubqueryScan:
1199 case T_FunctionScan:
1200 case T_TableFuncScan:
1201 case T_ValuesScan:
1202 case T_CteScan:
1203 case T_NamedTuplestoreScan:
1204 case T_WorkTableScan:
1205 *rels_used = bms_add_member(*rels_used,
1206 ((Scan *) plan)->scanrelid);
1207 break;
1208 case T_ForeignScan:
1209 *rels_used = bms_add_members(*rels_used,
1210 ((ForeignScan *) plan)->fs_base_relids);
1211 break;
1212 case T_CustomScan:
1213 *rels_used = bms_add_members(*rels_used,
1214 ((CustomScan *) plan)->custom_relids);
1215 break;
1216 case T_ModifyTable:
1217 *rels_used = bms_add_member(*rels_used,
1218 ((ModifyTable *) plan)->nominalRelation);
1219 if (((ModifyTable *) plan)->exclRelRTI)
1220 *rels_used = bms_add_member(*rels_used,
1221 ((ModifyTable *) plan)->exclRelRTI);
1222 /* Ensure Vars used in RETURNING will have refnames */
1223 if (plan->targetlist)
1224 *rels_used = bms_add_member(*rels_used,
1225 linitial_int(((ModifyTable *) plan)->resultRelations));
1226 break;
1227 case T_Append:
1228 *rels_used = bms_add_members(*rels_used,
1229 ((Append *) plan)->apprelids);
1230 break;
1231 case T_MergeAppend:
1232 *rels_used = bms_add_members(*rels_used,
1233 ((MergeAppend *) plan)->apprelids);
1234 break;
1235 case T_Result:
1236 *rels_used = bms_add_members(*rels_used,
1237 ((Result *) plan)->relids);
1238 break;
1239 default:
1240 break;
1241 }
1242
1243 return planstate_tree_walker(planstate, ExplainPreScanNode, rels_used);
1244}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:814
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:916
static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
Definition: explain.c:1185
#define planstate_tree_walker(ps, w, c)
Definition: nodeFuncs.h:179
#define linitial_int(l)
Definition: pg_list.h:179

References bms_add_member(), bms_add_members(), ExplainPreScanNode(), linitial_int, 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 902 of file explain.c.

903{
904 instr_time total_time;
905
906 /* don't print information if no JITing happened */
907 if (!ji || ji->created_functions == 0)
908 return;
909
910 /* calculate total time */
911 INSTR_TIME_SET_ZERO(total_time);
912 /* don't add deform_counter, it's included in generation_counter */
913 INSTR_TIME_ADD(total_time, ji->generation_counter);
914 INSTR_TIME_ADD(total_time, ji->inlining_counter);
915 INSTR_TIME_ADD(total_time, ji->optimization_counter);
916 INSTR_TIME_ADD(total_time, ji->emission_counter);
917
918 ExplainOpenGroup("JIT", "JIT", true, es);
919
920 /* for higher density, open code the text output format */
921 if (es->format == EXPLAIN_FORMAT_TEXT)
922 {
924 appendStringInfoString(es->str, "JIT:\n");
925 es->indent++;
926
927 ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
928
930 appendStringInfo(es->str, "Options: %s %s, %s %s, %s %s, %s %s\n",
931 "Inlining", jit_flags & PGJIT_INLINE ? "true" : "false",
932 "Optimization", jit_flags & PGJIT_OPT3 ? "true" : "false",
933 "Expressions", jit_flags & PGJIT_EXPR ? "true" : "false",
934 "Deforming", jit_flags & PGJIT_DEFORM ? "true" : "false");
935
936 if (es->analyze && es->timing)
937 {
940 "Timing: %s %.3f ms (%s %.3f ms), %s %.3f ms, %s %.3f ms, %s %.3f ms, %s %.3f ms\n",
941 "Generation", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->generation_counter),
942 "Deform", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->deform_counter),
943 "Inlining", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->inlining_counter),
944 "Optimization", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->optimization_counter),
945 "Emission", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->emission_counter),
946 "Total", 1000.0 * INSTR_TIME_GET_DOUBLE(total_time));
947 }
948
949 es->indent--;
950 }
951 else
952 {
953 ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
954
955 ExplainOpenGroup("Options", "Options", true, es);
956 ExplainPropertyBool("Inlining", jit_flags & PGJIT_INLINE, es);
957 ExplainPropertyBool("Optimization", jit_flags & PGJIT_OPT3, es);
958 ExplainPropertyBool("Expressions", jit_flags & PGJIT_EXPR, es);
959 ExplainPropertyBool("Deforming", jit_flags & PGJIT_DEFORM, es);
960 ExplainCloseGroup("Options", "Options", true, es);
961
962 if (es->analyze && es->timing)
963 {
964 ExplainOpenGroup("Timing", "Timing", true, es);
965
966 ExplainOpenGroup("Generation", "Generation", true, es);
967 ExplainPropertyFloat("Deform", "ms",
969 3, es);
970 ExplainPropertyFloat("Total", "ms",
972 3, es);
973 ExplainCloseGroup("Generation", "Generation", true, es);
974
975 ExplainPropertyFloat("Inlining", "ms",
977 3, es);
978 ExplainPropertyFloat("Optimization", "ms",
980 3, es);
981 ExplainPropertyFloat("Emission", "ms",
983 3, es);
984 ExplainPropertyFloat("Total", "ms",
985 1000.0 * INSTR_TIME_GET_DOUBLE(total_time),
986 3, es);
987
988 ExplainCloseGroup("Timing", "Timing", true, es);
989 }
990 }
991
992 ExplainCloseGroup("JIT", "JIT", true, es);
993}
#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 876 of file explain.c.

877{
878 JitInstrumentation ji = {0};
879
880 if (!(queryDesc->estate->es_jit_flags & PGJIT_PERFORM))
881 return;
882
883 /*
884 * Work with a copy instead of modifying the leader state, since this
885 * function may be called twice
886 */
887 if (queryDesc->estate->es_jit)
888 InstrJitAgg(&ji, &queryDesc->estate->es_jit->instr);
889
890 /* If this process has done JIT in parallel workers, merge stats */
891 if (queryDesc->estate->es_jit_worker_instr)
892 InstrJitAgg(&ji, queryDesc->estate->es_jit_worker_instr);
893
894 ExplainPrintJIT(es, queryDesc->estate->es_jit_flags, &ji);
895}
void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
Definition: jit.c:182
#define PGJIT_PERFORM
Definition: jit.h:20
struct JitContext * es_jit
Definition: execnodes.h:764
struct JitInstrumentation * es_jit_worker_instr
Definition: execnodes.h:765
JitInstrumentation instr
Definition: jit.h:62
EState * estate
Definition: execdesc.h:48

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 760 of file explain.c.

761{
762 Bitmapset *rels_used = NULL;
763 PlanState *ps;
764 ListCell *lc;
765
766 /* Set up ExplainState fields associated with this plan tree */
767 Assert(queryDesc->plannedstmt != NULL);
768 es->pstmt = queryDesc->plannedstmt;
769 es->rtable = queryDesc->plannedstmt->rtable;
770 ExplainPreScanNode(queryDesc->planstate, &rels_used);
773 es->rtable_names);
774 es->printed_subplans = NULL;
775 es->rtable_size = list_length(es->rtable);
776 foreach(lc, es->rtable)
777 {
779
780 if (rte->rtekind == RTE_GROUP)
781 {
782 es->rtable_size--;
783 break;
784 }
785 }
786
787 /*
788 * Sometimes we mark a Gather node as "invisible", which means that it's
789 * not to be displayed in EXPLAIN output. The purpose of this is to allow
790 * running regression tests with debug_parallel_query=regress to get the
791 * same results as running the same tests with debug_parallel_query=off.
792 * Such marking is currently only supported on a Gather at the top of the
793 * plan. We skip that node, and we must also hide per-worker detail data
794 * further down in the plan tree.
795 */
796 ps = queryDesc->planstate;
797 if (IsA(ps, GatherState) && ((Gather *) ps->plan)->invisible)
798 {
800 es->hide_workers = true;
801 }
802 ExplainNode(ps, NIL, NULL, NULL, es);
803
804 /*
805 * If requested, include information about GUC parameters with values that
806 * don't match the built-in defaults.
807 */
809
810 /*
811 * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_AUTO, but we don't show
812 * the queryid in any of the EXPLAIN plans to keep stable the results
813 * generated by regression test suites.
814 */
815 if (es->verbose && queryDesc->plannedstmt->queryId != INT64CONST(0) &&
817 {
818 ExplainPropertyInteger("Query Identifier", NULL,
819 queryDesc->plannedstmt->queryId, es);
820 }
821}
#define INT64CONST(x)
Definition: c.h:566
static void ExplainPrintSettings(ExplainState *es)
Definition: explain.c:690
struct parser_state ps
@ RTE_GROUP
Definition: parsenodes.h:1081
#define lfirst_node(type, lc)
Definition: pg_list.h:176
@ COMPUTE_QUERY_ID_REGRESS
Definition: queryjumble.h:86
int compute_query_id
List * deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
Definition: ruleutils.c:3756
List * select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
Definition: ruleutils.c:3858
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
int64 queryId
Definition: plannodes.h:71
List * rtable
Definition: plannodes.h:109
PlannedStmt * plannedstmt
Definition: execdesc.h:37
PlanState * planstate
Definition: execdesc.h:49
RTEKind rtekind
Definition: parsenodes.h:1105

References Assert(), compute_query_id, COMPUTE_QUERY_ID_REGRESS, deparse_context_for_plan_tree(), ExplainState::deparse_cxt, ExplainNode(), ExplainPreScanNode(), ExplainPrintSettings(), ExplainPropertyInteger(), ExplainState::hide_workers, INT64CONST, 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(), and ExplainState::verbose.

Referenced by explain_ExecutorEnd(), and ExplainOnePlan().

◆ ExplainPrintSerialize()

static void ExplainPrintSerialize ( ExplainState es,
SerializeMetrics metrics 
)
static

Definition at line 1000 of file explain.c.

1001{
1002 const char *format;
1003
1004 /* We shouldn't get called for EXPLAIN_SERIALIZE_NONE */
1006 format = "text";
1007 else
1008 {
1010 format = "binary";
1011 }
1012
1013 ExplainOpenGroup("Serialization", "Serialization", true, es);
1014
1015 if (es->format == EXPLAIN_FORMAT_TEXT)
1016 {
1018 if (es->timing)
1019 appendStringInfo(es->str, "Serialization: time=%.3f ms output=" UINT64_FORMAT "kB format=%s\n",
1020 1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
1021 BYTES_TO_KILOBYTES(metrics->bytesSent),
1022 format);
1023 else
1024 appendStringInfo(es->str, "Serialization: output=" UINT64_FORMAT "kB format=%s\n",
1025 BYTES_TO_KILOBYTES(metrics->bytesSent),
1026 format);
1027
1028 if (es->buffers && peek_buffer_usage(es, &metrics->bufferUsage))
1029 {
1030 es->indent++;
1031 show_buffer_usage(es, &metrics->bufferUsage);
1032 es->indent--;
1033 }
1034 }
1035 else
1036 {
1037 if (es->timing)
1038 ExplainPropertyFloat("Time", "ms",
1039 1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
1040 3, es);
1041 ExplainPropertyUInteger("Output Volume", "kB",
1042 BYTES_TO_KILOBYTES(metrics->bytesSent), es);
1043 ExplainPropertyText("Format", format, es);
1044 if (es->buffers)
1045 show_buffer_usage(es, &metrics->bufferUsage);
1046 }
1047
1048 ExplainCloseGroup("Serialization", "Serialization", true, es);
1049}
#define UINT64_FORMAT
Definition: c.h:571
#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:25
instr_time timeSpent
Definition: explain_dr.h:26
BufferUsage bufferUsage
Definition: explain_dr.h:27

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 690 of file explain.c.

691{
692 int num;
693 struct config_generic **gucs;
694
695 /* bail out if information about settings not requested */
696 if (!es->settings)
697 return;
698
699 /* request an array of relevant settings */
700 gucs = get_explain_guc_options(&num);
701
702 if (es->format != EXPLAIN_FORMAT_TEXT)
703 {
704 ExplainOpenGroup("Settings", "Settings", true, es);
705
706 for (int i = 0; i < num; i++)
707 {
708 char *setting;
709 struct config_generic *conf = gucs[i];
710
711 setting = GetConfigOptionByName(conf->name, NULL, true);
712
713 ExplainPropertyText(conf->name, setting, es);
714 }
715
716 ExplainCloseGroup("Settings", "Settings", true, es);
717 }
718 else
719 {
721
722 /* In TEXT mode, print nothing if there are no options */
723 if (num <= 0)
724 return;
725
727
728 for (int i = 0; i < num; i++)
729 {
730 char *setting;
731 struct config_generic *conf = gucs[i];
732
733 if (i > 0)
735
736 setting = GetConfigOptionByName(conf->name, NULL, true);
737
738 if (setting)
739 appendStringInfo(&str, "%s = '%s'", conf->name, setting);
740 else
741 appendStringInfo(&str, "%s = NULL", conf->name);
742 }
743
744 ExplainPropertyText("Settings", str.data, es);
745 }
746}
char * GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
Definition: guc.c:5301
struct config_generic ** get_explain_guc_options(int *num)
Definition: guc.c:5200
const char * str
const char * name
Definition: guc_tables.h:252

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 833 of file explain.c.

834{
835 ResultRelInfo *rInfo;
836 bool show_relname;
837 List *resultrels;
838 List *routerels;
839 List *targrels;
840 ListCell *l;
841
842 resultrels = queryDesc->estate->es_opened_result_relations;
843 routerels = queryDesc->estate->es_tuple_routing_result_relations;
844 targrels = queryDesc->estate->es_trig_target_relations;
845
846 ExplainOpenGroup("Triggers", "Triggers", false, es);
847
848 show_relname = (list_length(resultrels) > 1 ||
849 routerels != NIL || targrels != NIL);
850 foreach(l, resultrels)
851 {
852 rInfo = (ResultRelInfo *) lfirst(l);
853 report_triggers(rInfo, show_relname, es);
854 }
855
856 foreach(l, routerels)
857 {
858 rInfo = (ResultRelInfo *) lfirst(l);
859 report_triggers(rInfo, show_relname, es);
860 }
861
862 foreach(l, targrels)
863 {
864 rInfo = (ResultRelInfo *) lfirst(l);
865 report_triggers(rInfo, show_relname, es);
866 }
867
868 ExplainCloseGroup("Triggers", "Triggers", false, es);
869}
static void report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
Definition: explain.c:1093
List * es_tuple_routing_result_relations
Definition: execnodes.h:698
List * es_trig_target_relations
Definition: execnodes.h:701
List * es_opened_result_relations
Definition: execnodes.h:688

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 177 of file explain.c.

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

1076{
1077 char *str;
1078
1079 /* This check is consistent with errdetail_params() */
1080 if (params == NULL || params->numParams <= 0 || maxlen == 0)
1081 return;
1082
1083 str = BuildParamLogString(params, NULL, maxlen);
1084 if (str && str[0] != '\0')
1085 ExplainPropertyText("Query Parameters", str, es);
1086}
char * BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen)
Definition: params.c:333

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

Referenced by explain_ExecutorEnd().

◆ ExplainQueryText()

void ExplainQueryText ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 1060 of file explain.c.

1061{
1062 if (queryDesc->sourceText)
1063 ExplainPropertyText("Query Text", queryDesc->sourceText, es);
1064}
const char * sourceText
Definition: execdesc.h:38

References ExplainPropertyText(), and QueryDesc::sourceText.

Referenced by explain_ExecutorEnd().

◆ ExplainResultDesc()

TupleDesc ExplainResultDesc ( ExplainStmt stmt)

Definition at line 255 of file explain.c.

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

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 4393 of file explain.c.

4394{
4395 ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
4396}

References ExplainTargetRel(), and plan.

Referenced by ExplainNode().

◆ ExplainSubPlans()

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

Definition at line 4903 of file explain.c.

4905{
4906 ListCell *lst;
4907
4908 foreach(lst, plans)
4909 {
4910 SubPlanState *sps = (SubPlanState *) lfirst(lst);
4911 SubPlan *sp = sps->subplan;
4912 char *cooked_plan_name;
4913
4914 /*
4915 * There can be multiple SubPlan nodes referencing the same physical
4916 * subplan (same plan_id, which is its index in PlannedStmt.subplans).
4917 * We should print a subplan only once, so track which ones we already
4918 * printed. This state must be global across the plan tree, since the
4919 * duplicate nodes could be in different plan nodes, eg both a bitmap
4920 * indexscan's indexqual and its parent heapscan's recheck qual. (We
4921 * do not worry too much about which plan node we show the subplan as
4922 * attached to in such cases.)
4923 */
4925 continue;
4927 sp->plan_id);
4928
4929 /*
4930 * Treat the SubPlan node as an ancestor of the plan node(s) within
4931 * it, so that ruleutils.c can find the referents of subplan
4932 * parameters.
4933 */
4934 ancestors = lcons(sp, ancestors);
4935
4936 /*
4937 * The plan has a name like exists_1 or rowcompare_2, but here we want
4938 * to prefix that with CTE, InitPlan, or SubPlan, as appropriate, for
4939 * display purposes.
4940 */
4941 if (sp->subLinkType == CTE_SUBLINK)
4942 cooked_plan_name = psprintf("CTE %s", sp->plan_name);
4943 else if (sp->isInitPlan)
4944 cooked_plan_name = psprintf("InitPlan %s", sp->plan_name);
4945 else
4946 cooked_plan_name = psprintf("SubPlan %s", sp->plan_name);
4947
4948 ExplainNode(sps->planstate, ancestors,
4949 relationship, cooked_plan_name, es);
4950
4951 ancestors = list_delete_first(ancestors);
4952 }
4953}
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
@ CTE_SUBLINK
Definition: primnodes.h:1036
PlanState * planstate
Definition: execnodes.h:1013
SubPlan * subplan
Definition: execnodes.h:1012
int plan_id
Definition: primnodes.h:1102
char * plan_name
Definition: primnodes.h:1104
bool isInitPlan
Definition: primnodes.h:1111
SubLinkType subLinkType
Definition: primnodes.h:1097

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

Referenced by ExplainNode().

◆ ExplainTargetRel()

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

Definition at line 4415 of file explain.c.

4416{
4417 char *objectname = NULL;
4418 char *namespace = NULL;
4419 const char *objecttag = NULL;
4420 RangeTblEntry *rte;
4421 char *refname;
4422
4423 rte = rt_fetch(rti, es->rtable);
4424 refname = (char *) list_nth(es->rtable_names, rti - 1);
4425 if (refname == NULL)
4426 refname = rte->eref->aliasname;
4427
4428 switch (nodeTag(plan))
4429 {
4430 case T_SeqScan:
4431 case T_SampleScan:
4432 case T_IndexScan:
4433 case T_IndexOnlyScan:
4434 case T_BitmapHeapScan:
4435 case T_TidScan:
4436 case T_TidRangeScan:
4437 case T_ForeignScan:
4438 case T_CustomScan:
4439 case T_ModifyTable:
4440 /* Assert it's on a real relation */
4441 Assert(rte->rtekind == RTE_RELATION);
4442 objectname = get_rel_name(rte->relid);
4443 if (es->verbose)
4444 namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid));
4445 objecttag = "Relation Name";
4446 break;
4447 case T_FunctionScan:
4448 {
4449 FunctionScan *fscan = (FunctionScan *) plan;
4450
4451 /* Assert it's on a RangeFunction */
4452 Assert(rte->rtekind == RTE_FUNCTION);
4453
4454 /*
4455 * If the expression is still a function call of a single
4456 * function, we can get the real name of the function.
4457 * Otherwise, punt. (Even if it was a single function call
4458 * originally, the optimizer could have simplified it away.)
4459 */
4460 if (list_length(fscan->functions) == 1)
4461 {
4463
4464 if (IsA(rtfunc->funcexpr, FuncExpr))
4465 {
4466 FuncExpr *funcexpr = (FuncExpr *) rtfunc->funcexpr;
4467 Oid funcid = funcexpr->funcid;
4468
4469 objectname = get_func_name(funcid);
4470 if (es->verbose)
4472 }
4473 }
4474 objecttag = "Function Name";
4475 }
4476 break;
4477 case T_TableFuncScan:
4478 {
4479 TableFunc *tablefunc = ((TableFuncScan *) plan)->tablefunc;
4480
4481 Assert(rte->rtekind == RTE_TABLEFUNC);
4482 switch (tablefunc->functype)
4483 {
4484 case TFT_XMLTABLE:
4485 objectname = "xmltable";
4486 break;
4487 case TFT_JSON_TABLE:
4488 objectname = "json_table";
4489 break;
4490 default:
4491 elog(ERROR, "invalid TableFunc type %d",
4492 (int) tablefunc->functype);
4493 }
4494 objecttag = "Table Function Name";
4495 }
4496 break;
4497 case T_ValuesScan:
4498 Assert(rte->rtekind == RTE_VALUES);
4499 break;
4500 case T_CteScan:
4501 /* Assert it's on a non-self-reference CTE */
4502 Assert(rte->rtekind == RTE_CTE);
4503 Assert(!rte->self_reference);
4504 objectname = rte->ctename;
4505 objecttag = "CTE Name";
4506 break;
4507 case T_NamedTuplestoreScan:
4509 objectname = rte->enrname;
4510 objecttag = "Tuplestore Name";
4511 break;
4512 case T_WorkTableScan:
4513 /* Assert it's on a self-reference CTE */
4514 Assert(rte->rtekind == RTE_CTE);
4515 Assert(rte->self_reference);
4516 objectname = rte->ctename;
4517 objecttag = "CTE Name";
4518 break;
4519 default:
4520 break;
4521 }
4522
4523 if (es->format == EXPLAIN_FORMAT_TEXT)
4524 {
4525 appendStringInfoString(es->str, " on");
4526 if (namespace != NULL)
4527 appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
4528 quote_identifier(objectname));
4529 else if (objectname != NULL)
4530 appendStringInfo(es->str, " %s", quote_identifier(objectname));
4531 if (objectname == NULL || strcmp(refname, objectname) != 0)
4532 appendStringInfo(es->str, " %s", quote_identifier(refname));
4533 }
4534 else
4535 {
4536 if (objecttag != NULL && objectname != NULL)
4537 ExplainPropertyText(objecttag, objectname, es);
4538 if (namespace != NULL)
4539 ExplainPropertyText("Schema", namespace, es);
4540 ExplainPropertyText("Alias", refname, es);
4541 }
4542}
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:2102
char * get_namespace_name_or_temp(Oid nspid)
Definition: lsyscache.c:3540
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1758
Oid get_func_namespace(Oid funcid)
Definition: lsyscache.c:1782
@ RTE_CTE
Definition: parsenodes.h:1076
@ RTE_NAMEDTUPLESTORE
Definition: parsenodes.h:1077
@ RTE_VALUES
Definition: parsenodes.h:1075
@ RTE_FUNCTION
Definition: parsenodes.h:1073
@ RTE_TABLEFUNC
Definition: parsenodes.h:1074
@ RTE_RELATION
Definition: parsenodes.h:1070
#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:782
List * functions
Definition: plannodes.h:767
char * ctename
Definition: parsenodes.h:1254
char * enrname
Definition: parsenodes.h:1289
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(), 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 4073 of file explain.c.

4074{
4075 bool has_shared;
4076 bool has_local;
4077 bool has_temp;
4078 bool has_shared_timing;
4079 bool has_local_timing;
4080 bool has_temp_timing;
4081
4082 if (usage == NULL)
4083 return false;
4084
4085 if (es->format != EXPLAIN_FORMAT_TEXT)
4086 return true;
4087
4088 has_shared = (usage->shared_blks_hit > 0 ||
4089 usage->shared_blks_read > 0 ||
4090 usage->shared_blks_dirtied > 0 ||
4091 usage->shared_blks_written > 0);
4092 has_local = (usage->local_blks_hit > 0 ||
4093 usage->local_blks_read > 0 ||
4094 usage->local_blks_dirtied > 0 ||
4095 usage->local_blks_written > 0);
4096 has_temp = (usage->temp_blks_read > 0 ||
4097 usage->temp_blks_written > 0);
4098 has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
4099 !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
4100 has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
4101 !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
4102 has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
4103 !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
4104
4105 return has_shared || has_local || has_temp || has_shared_timing ||
4106 has_local_timing || has_temp_timing;
4107}
#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 1253 of file explain.c.

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

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 1093 of file explain.c.

1094{
1095 int nt;
1096
1097 if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
1098 return;
1099 for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
1100 {
1101 Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
1102 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
1103 char *relname;
1104 char *conname = NULL;
1105
1106 /* Must clean up instrumentation state */
1107 InstrEndLoop(instr);
1108
1109 /*
1110 * We ignore triggers that were never invoked; they likely aren't
1111 * relevant to the current query type.
1112 */
1113 if (instr->ntuples == 0)
1114 continue;
1115
1116 ExplainOpenGroup("Trigger", NULL, true, es);
1117
1119 if (OidIsValid(trig->tgconstraint))
1120 conname = get_constraint_name(trig->tgconstraint);
1121
1122 /*
1123 * In text format, we avoid printing both the trigger name and the
1124 * constraint name unless VERBOSE is specified. In non-text formats
1125 * we just print everything.
1126 */
1127 if (es->format == EXPLAIN_FORMAT_TEXT)
1128 {
1129 if (es->verbose || conname == NULL)
1130 appendStringInfo(es->str, "Trigger %s", trig->tgname);
1131 else
1132 appendStringInfoString(es->str, "Trigger");
1133 if (conname)
1134 appendStringInfo(es->str, " for constraint %s", conname);
1135 if (show_relname)
1136 appendStringInfo(es->str, " on %s", relname);
1137 if (es->timing)
1138 appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
1140 instr->ntuples);
1141 else
1142 appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
1143 }
1144 else
1145 {
1146 ExplainPropertyText("Trigger Name", trig->tgname, es);
1147 if (conname)
1148 ExplainPropertyText("Constraint Name", conname, es);
1149 ExplainPropertyText("Relation", relname, es);
1150 if (es->timing)
1151 ExplainPropertyFloat("Time", "ms",
1152 INSTR_TIME_GET_MILLISEC(instr->total), 3,
1153 es);
1154 ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
1155 }
1156
1157 if (conname)
1158 pfree(conname);
1159
1160 ExplainCloseGroup("Trigger", NULL, true, es);
1161 }
1162}
#define OidIsValid(objectId)
Definition: c.h:794
char * get_constraint_name(Oid conoid)
Definition: lsyscache.c:1157
NameData relname
Definition: pg_class.h:38
#define RelationGetRelationName(relation)
Definition: rel.h:549
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:524
Relation ri_RelationDesc
Definition: execnodes.h:480
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:515
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(), INSTR_TIME_GET_MILLISEC, 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 2624 of file explain.c.

2626{
2627 Agg *plan = (Agg *) astate->ss.ps.plan;
2628
2629 if (plan->numCols > 0 || plan->groupingSets)
2630 {
2631 /* The key columns refer to the tlist of the child plan */
2632 ancestors = lcons(plan, ancestors);
2633
2634 if (plan->groupingSets)
2635 show_grouping_sets(outerPlanState(astate), plan, ancestors, es);
2636 else
2637 show_sort_group_keys(outerPlanState(astate), "Group Key",
2638 plan->numCols, 0, plan->grpColIdx,
2639 NULL, NULL, NULL,
2640 ancestors, es);
2641
2642 ancestors = list_delete_first(ancestors);
2643 }
2644}
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:2776
static void show_grouping_sets(PlanState *planstate, Agg *agg, List *ancestors, ExplainState *es)
Definition: explain.c:2647
ScanState ss
Definition: execnodes.h:2535
PlanState ps
Definition: execnodes.h:1621

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 4113 of file explain.c.

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

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 3521 of file explain.c.

3522{
3523 char *maxStorageType;
3524 int64 maxSpaceUsed;
3525
3526 Tuplestorestate *tupstore = ctescanstate->leader->cte_table;
3527
3528 if (!es->analyze || tupstore == NULL)
3529 return;
3530
3531 tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
3532 show_storage_info(maxStorageType, maxSpaceUsed, es);
3533}
int64_t int64
Definition: c.h:549
static void show_storage_info(char *maxStorageType, int64 maxSpaceUsed, ExplainState *es)
Definition: explain.c:3003
Tuplestorestate * cte_table
Definition: execnodes.h:2069
struct CteScanState * leader
Definition: execnodes.h:2067
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 2504 of file explain.c.

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

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 4021 of file explain.c.

4022{
4023 FdwRoutine *fdwroutine = fsstate->fdwroutine;
4024
4025 /* Let the FDW emit whatever fields it wants */
4026 if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
4027 {
4028 if (fdwroutine->ExplainDirectModify != NULL)
4029 fdwroutine->ExplainDirectModify(fsstate, es);
4030 }
4031 else
4032 {
4033 if (fdwroutine->ExplainForeignScan != NULL)
4034 fdwroutine->ExplainForeignScan(fsstate, es);
4035 }
4036}
ExplainForeignScan_function ExplainForeignScan
Definition: fdwapi.h:252
ExplainDirectModify_function ExplainDirectModify
Definition: fdwapi.h:254
struct FdwRoutine * fdwroutine
Definition: execnodes.h:2118
ScanState ss
Definition: execnodes.h:2113

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 2756 of file explain.c.

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

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 2678 of file explain.c.

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

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 2647 of file explain.c.

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

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 3383 of file explain.c.

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

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 3743 of file explain.c.

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

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 3183 of file explain.c.

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

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

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 2591 of file explain.c.

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

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 3865 of file explain.c.

3866{
3867 Plan *plan = planstate->plan;
3868 SharedIndexScanInstrumentation *SharedInfo = NULL;
3869 uint64 nsearches = 0;
3870
3871 if (!es->analyze)
3872 return;
3873
3874 /* Initialize counters with stats from the local process first */
3875 switch (nodeTag(plan))
3876 {
3877 case T_IndexScan:
3878 {
3879 IndexScanState *indexstate = ((IndexScanState *) planstate);
3880
3881 nsearches = indexstate->iss_Instrument.nsearches;
3882 SharedInfo = indexstate->iss_SharedInfo;
3883 break;
3884 }
3885 case T_IndexOnlyScan:
3886 {
3887 IndexOnlyScanState *indexstate = ((IndexOnlyScanState *) planstate);
3888
3889 nsearches = indexstate->ioss_Instrument.nsearches;
3890 SharedInfo = indexstate->ioss_SharedInfo;
3891 break;
3892 }
3893 case T_BitmapIndexScan:
3894 {
3895 BitmapIndexScanState *indexstate = ((BitmapIndexScanState *) planstate);
3896
3897 nsearches = indexstate->biss_Instrument.nsearches;
3898 SharedInfo = indexstate->biss_SharedInfo;
3899 break;
3900 }
3901 default:
3902 break;
3903 }
3904
3905 /* Next get the sum of the counters set within each and every process */
3906 if (SharedInfo)
3907 {
3908 for (int i = 0; i < SharedInfo->num_workers; ++i)
3909 {
3910 IndexScanInstrumentation *winstrument = &SharedInfo->winstrument[i];
3911
3912 nsearches += winstrument->nsearches;
3913 }
3914 }
3915
3916 ExplainPropertyUInteger("Index Searches", NULL, nsearches, es);
3917}
SharedIndexScanInstrumentation * biss_SharedInfo
Definition: execnodes.h:1816
IndexScanInstrumentation biss_Instrument
Definition: execnodes.h:1815
SharedIndexScanInstrumentation * ioss_SharedInfo
Definition: execnodes.h:1775
IndexScanInstrumentation ioss_Instrument
Definition: execnodes.h:1774
IndexScanInstrumentation iss_Instrument
Definition: execnodes.h:1723
SharedIndexScanInstrumentation * iss_SharedInfo
Definition: execnodes.h:1724
IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: genam.h:51

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 3992 of file explain.c.

3994{
3995 double nfiltered;
3996 double nloops;
3997
3998 if (!es->analyze || !planstate->instrument)
3999 return;
4000
4001 if (which == 2)
4002 nfiltered = planstate->instrument->nfiltered2;
4003 else
4004 nfiltered = planstate->instrument->nfiltered1;
4005 nloops = planstate->instrument->nloops;
4006
4007 /* In text mode, suppress zero counts; they're not interesting enough */
4008 if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
4009 {
4010 if (nloops > 0)
4011 ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es);
4012 else
4013 ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es);
4014 }
4015}
double nfiltered2
Definition: instrument.h:92
double nfiltered1
Definition: instrument.h:91

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 3475 of file explain.c.

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

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 3590 of file explain.c.

3591{
3592 Plan *plan = ((PlanState *) mstate)->plan;
3593 Memoize *mplan = (Memoize *) plan;
3594 ListCell *lc;
3595 List *context;
3596 StringInfoData keystr;
3597 char *separator = "";
3598 bool useprefix;
3599 int64 memPeakKb;
3600
3601 initStringInfo(&keystr);
3602
3603 /*
3604 * It's hard to imagine having a memoize node with fewer than 2 RTEs, but
3605 * let's just keep the same useprefix logic as elsewhere in this file.
3606 */
3607 useprefix = es->rtable_size > 1 || es->verbose;
3608
3609 /* Set up deparsing context */
3611 plan,
3612 ancestors);
3613
3614 foreach(lc, mplan->param_exprs)
3615 {
3616 Node *expr = (Node *) lfirst(lc);
3617
3619
3620 appendStringInfoString(&keystr, deparse_expression(expr, context,
3621 useprefix, false));
3622 separator = ", ";
3623 }
3624
3625 ExplainPropertyText("Cache Key", keystr.data, es);
3626 ExplainPropertyText("Cache Mode", mstate->binary_mode ? "binary" : "logical", es);
3627
3628 pfree(keystr.data);
3629
3630 if (es->costs)
3631 {
3632 if (es->format == EXPLAIN_FORMAT_TEXT)
3633 {
3635 appendStringInfo(es->str, "Estimates: capacity=%u distinct keys=%.0f lookups=%.0f hit percent=%.2f%%\n",
3636 mplan->est_entries, mplan->est_unique_keys,
3637 mplan->est_calls, mplan->est_hit_ratio * 100.0);
3638 }
3639 else
3640 {
3641 ExplainPropertyUInteger("Estimated Capacity", NULL, mplan->est_entries, es);
3642 ExplainPropertyFloat("Estimated Distinct Lookup Keys", NULL, mplan->est_unique_keys, 0, es);
3643 ExplainPropertyFloat("Estimated Lookups", NULL, mplan->est_calls, 0, es);
3644 ExplainPropertyFloat("Estimated Hit Percent", NULL, mplan->est_hit_ratio * 100.0, 2, es);
3645 }
3646 }
3647
3648 if (!es->analyze)
3649 return;
3650
3651 if (mstate->stats.cache_misses > 0)
3652 {
3653 /*
3654 * mem_peak is only set when we freed memory, so we must use mem_used
3655 * when mem_peak is 0.
3656 */
3657 if (mstate->stats.mem_peak > 0)
3658 memPeakKb = BYTES_TO_KILOBYTES(mstate->stats.mem_peak);
3659 else
3660 memPeakKb = BYTES_TO_KILOBYTES(mstate->mem_used);
3661
3662 if (es->format != EXPLAIN_FORMAT_TEXT)
3663 {
3664 ExplainPropertyInteger("Cache Hits", NULL, mstate->stats.cache_hits, es);
3665 ExplainPropertyInteger("Cache Misses", NULL, mstate->stats.cache_misses, es);
3666 ExplainPropertyInteger("Cache Evictions", NULL, mstate->stats.cache_evictions, es);
3667 ExplainPropertyInteger("Cache Overflows", NULL, mstate->stats.cache_overflows, es);
3668 ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
3669 }
3670 else
3671 {
3674 "Hits: " UINT64_FORMAT " Misses: " UINT64_FORMAT " Evictions: " UINT64_FORMAT " Overflows: " UINT64_FORMAT " Memory Usage: " INT64_FORMAT "kB\n",
3675 mstate->stats.cache_hits,
3676 mstate->stats.cache_misses,
3677 mstate->stats.cache_evictions,
3678 mstate->stats.cache_overflows,
3679 memPeakKb);
3680 }
3681 }
3682
3683 if (mstate->shared_info == NULL)
3684 return;
3685
3686 /* Show details from parallel workers */
3687 for (int n = 0; n < mstate->shared_info->num_workers; n++)
3688 {
3690
3691 si = &mstate->shared_info->sinstrument[n];
3692
3693 /*
3694 * Skip workers that didn't do any work. We needn't bother checking
3695 * for cache hits as a miss will always occur before a cache hit.
3696 */
3697 if (si->cache_misses == 0)
3698 continue;
3699
3700 if (es->workers_state)
3701 ExplainOpenWorker(n, es);
3702
3703 /*
3704 * Since the worker's MemoizeState.mem_used field is unavailable to
3705 * us, ExecEndMemoize will have set the
3706 * MemoizeInstrumentation.mem_peak field for us. No need to do the
3707 * zero checks like we did for the serial case above.
3708 */
3709 memPeakKb = BYTES_TO_KILOBYTES(si->mem_peak);
3710
3711 if (es->format == EXPLAIN_FORMAT_TEXT)
3712 {
3715 "Hits: " UINT64_FORMAT " Misses: " UINT64_FORMAT " Evictions: " UINT64_FORMAT " Overflows: " UINT64_FORMAT " Memory Usage: " INT64_FORMAT "kB\n",
3716 si->cache_hits, si->cache_misses,
3718 memPeakKb);
3719 }
3720 else
3721 {
3722 ExplainPropertyInteger("Cache Hits", NULL,
3723 si->cache_hits, es);
3724 ExplainPropertyInteger("Cache Misses", NULL,
3725 si->cache_misses, es);
3726 ExplainPropertyInteger("Cache Evictions", NULL,
3727 si->cache_evictions, es);
3728 ExplainPropertyInteger("Cache Overflows", NULL,
3729 si->cache_overflows, es);
3730 ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
3731 es);
3732 }
3733
3734 if (es->workers_state)
3735 ExplainCloseWorker(n, es);
3736 }
3737}
uint64 mem_used
Definition: execnodes.h:2354
SharedMemoizeInfo * shared_info
Definition: execnodes.h:2369
MemoizeInstrumentation stats
Definition: execnodes.h:2368
bool binary_mode
Definition: execnodes.h:2366
Cardinality est_calls
Definition: plannodes.h:1108
Cardinality est_unique_keys
Definition: plannodes.h:1111
List * param_exprs
Definition: plannodes.h:1084
double est_hit_ratio
Definition: plannodes.h:1114
uint32 est_entries
Definition: plannodes.h:1102
MemoizeInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2330

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, ExplainState::costs, StringInfoData::data, ExplainState::deparse_cxt, deparse_expression(), Memoize::est_calls, Memoize::est_entries, Memoize::est_hit_ratio, Memoize::est_unique_keys, EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyText(), ExplainPropertyUInteger(), ExplainState::format, initStringInfo(), INT64_FORMAT, lfirst, MemoizeInstrumentation::mem_peak, MemoizeState::mem_used, SharedMemoizeInfo::num_workers, Memoize::param_exprs, 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 4331 of file explain.c.

4332{
4333 int64 memUsedkB = BYTES_TO_KILOBYTES(mem_counters->totalspace -
4334 mem_counters->freespace);
4335 int64 memAllocatedkB = BYTES_TO_KILOBYTES(mem_counters->totalspace);
4336
4337 if (es->format == EXPLAIN_FORMAT_TEXT)
4338 {
4341 "Memory: used=" INT64_FORMAT "kB allocated=" INT64_FORMAT "kB",
4342 memUsedkB, memAllocatedkB);
4343 appendStringInfoChar(es->str, '\n');
4344 }
4345 else
4346 {
4347 ExplainPropertyInteger("Memory Used", "kB", memUsedkB, es);
4348 ExplainPropertyInteger("Memory Allocated", "kB", memAllocatedkB, es);
4349 }
4350}

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 2608 of file explain.c.

2610{
2611 MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
2612
2613 show_sort_group_keys((PlanState *) mstate, "Sort Key",
2614 plan->numCols, 0, plan->sortColIdx,
2615 plan->sortOperators, plan->collations,
2616 plan->nullsFirst,
2617 ancestors, es);
2618}
PlanState ps
Definition: execnodes.h:1540

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 4553 of file explain.c.

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

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 2446 of file explain.c.

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

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 2527 of file explain.c.

2530{
2531 Node *node;
2532
2533 /* No work if empty qual */
2534 if (qual == NIL)
2535 return;
2536
2537 /* Convert AND list to explicit AND */
2538 node = (Node *) make_ands_explicit(qual);
2539
2540 /* And show it */
2541 show_expression(node, qlabel, planstate, ancestors, useprefix, es);
2542}
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 3559 of file explain.c.

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

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

Referenced by ExplainNode().

◆ show_result_replacement_info()

static void show_result_replacement_info ( Result result,
ExplainState es 
)
static

Definition at line 4771 of file explain.c.

4772{
4774 int nrels = 0;
4775 int rti = -1;
4776 bool found_non_result = false;
4777 char *replacement_type = "???";
4778
4779 /* If the Result node has a subplan, it didn't replace anything. */
4780 if (result->plan.lefttree != NULL)
4781 return;
4782
4783 /* Gating result nodes should have a subplan, and we don't. */
4785
4786 switch (result->result_type)
4787 {
4788 case RESULT_TYPE_GATING:
4789 replacement_type = "Gating";
4790 break;
4791 case RESULT_TYPE_SCAN:
4792 replacement_type = "Scan";
4793 break;
4794 case RESULT_TYPE_JOIN:
4795 replacement_type = "Join";
4796 break;
4797 case RESULT_TYPE_UPPER:
4798 /* a small white lie */
4799 replacement_type = "Aggregate";
4800 break;
4801 case RESULT_TYPE_MINMAX:
4802 replacement_type = "MinMaxAggregate";
4803 break;
4804 }
4805
4806 /*
4807 * Build up a comma-separated list of user-facing names for the range
4808 * table entries in the relids set.
4809 */
4811 while ((rti = bms_next_member(result->relids, rti)) >= 0)
4812 {
4813 RangeTblEntry *rte = rt_fetch(rti, es->rtable);
4814 char *refname;
4815
4816 /*
4817 * add_outer_joins_to_relids will add join RTIs to the relids set of a
4818 * join; if that join is then replaced with a Result node, we may see
4819 * such RTIs here. But we want to completely ignore those here,
4820 * because "a LEFT JOIN b ON whatever" is a join between a and b, not
4821 * a join between a, b, and an unnamed join.
4822 */
4823 if (rte->rtekind == RTE_JOIN)
4824 continue;
4825
4826 /* Count the number of rels that aren't ignored completely. */
4827 ++nrels;
4828
4829 /* Work out what reference name to use and add it to the string. */
4830 refname = (char *) list_nth(es->rtable_names, rti - 1);
4831 if (refname == NULL)
4832 refname = rte->eref->aliasname;
4833 if (buf.len > 0)
4835 appendStringInfoString(&buf, refname);
4836
4837 /* Keep track of whether we see anything other than RTE_RESULT. */
4838 if (rte->rtekind != RTE_RESULT)
4839 found_non_result = true;
4840 }
4841
4842 /*
4843 * If this Result node is because of a single RTE that is RTE_RESULT, it
4844 * is not really replacing anything at all, because there's no other
4845 * method for implementing a scan of such an RTE, so we don't display the
4846 * Replaces line in such cases.
4847 */
4848 if (nrels <= 1 && !found_non_result &&
4849 result->result_type == RESULT_TYPE_SCAN)
4850 return;
4851
4852 /* Say what we replaced, with list of rels if available. */
4853 if (buf.len == 0)
4854 ExplainPropertyText("Replaces", replacement_type, es);
4855 else
4856 {
4857 char *s = psprintf("%s on %s", replacement_type, buf.data);
4858
4859 ExplainPropertyText("Replaces", s, es);
4860 }
4861}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1305
@ RTE_JOIN
Definition: parsenodes.h:1072
@ RTE_RESULT
Definition: parsenodes.h:1078
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
@ RESULT_TYPE_UPPER
Definition: plannodes.h:274
@ RESULT_TYPE_SCAN
Definition: plannodes.h:272
@ RESULT_TYPE_GATING
Definition: plannodes.h:271
@ RESULT_TYPE_MINMAX
Definition: plannodes.h:275
@ RESULT_TYPE_JOIN
Definition: plannodes.h:273
ResultType result_type
Definition: plannodes.h:298
Bitmapset * relids
Definition: plannodes.h:300
Plan plan
Definition: plannodes.h:297

References appendStringInfoString(), Assert(), bms_next_member(), buf, ExplainPropertyText(), initStringInfo(), Plan::lefttree, list_nth(), Result::plan, psprintf(), Result::relids, Result::result_type, RESULT_TYPE_GATING, RESULT_TYPE_JOIN, RESULT_TYPE_MINMAX, RESULT_TYPE_SCAN, RESULT_TYPE_UPPER, rt_fetch, ExplainState::rtable, ExplainState::rtable_names, RTE_JOIN, RTE_RESULT, and RangeTblEntry::rtekind.

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 2548 of file explain.c.

2551{
2552 bool useprefix;
2553
2554 useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
2555 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2556}
static void show_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
Definition: explain.c:2527

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 2776 of file explain.c.

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

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

2577{
2578 Sort *plan = (Sort *) sortstate->ss.ps.plan;
2579
2580 show_sort_group_keys((PlanState *) sortstate, "Sort Key",
2581 plan->numCols, 0, plan->sortColIdx,
2582 plan->sortOperators, plan->collations,
2583 plan->nullsFirst,
2584 ancestors, es);
2585}
ScanState ss
Definition: execnodes.h:2404

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 2838 of file explain.c.

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

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 3003 of file explain.c.

3004{
3005 int64 maxSpaceUsedKB = BYTES_TO_KILOBYTES(maxSpaceUsed);
3006
3007 if (es->format != EXPLAIN_FORMAT_TEXT)
3008 {
3009 ExplainPropertyText("Storage", maxStorageType, es);
3010 ExplainPropertyInteger("Maximum Storage", "kB", maxSpaceUsedKB, es);
3011 }
3012 else
3013 {
3016 "Storage: %s Maximum Storage: " INT64_FORMAT "kB\n",
3017 maxStorageType,
3018 maxSpaceUsedKB);
3019 }
3020}

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 3540 of file explain.c.

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

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 3026 of file explain.c.

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

3924{
3925 if (!es->analyze)
3926 return;
3927
3928 if (es->format != EXPLAIN_FORMAT_TEXT)
3929 {
3930 ExplainPropertyUInteger("Exact Heap Blocks", NULL,
3931 planstate->stats.exact_pages, es);
3932 ExplainPropertyUInteger("Lossy Heap Blocks", NULL,
3933 planstate->stats.lossy_pages, es);
3934 }
3935 else
3936 {
3937 if (planstate->stats.exact_pages > 0 || planstate->stats.lossy_pages > 0)
3938 {
3940 appendStringInfoString(es->str, "Heap Blocks:");
3941 if (planstate->stats.exact_pages > 0)
3942 appendStringInfo(es->str, " exact=" UINT64_FORMAT, planstate->stats.exact_pages);
3943 if (planstate->stats.lossy_pages > 0)
3944 appendStringInfo(es->str, " lossy=" UINT64_FORMAT, planstate->stats.lossy_pages);
3945 appendStringInfoChar(es->str, '\n');
3946 }
3947 }
3948
3949 /* Display stats for each parallel worker */
3950 if (planstate->pstate != NULL)
3951 {
3952 for (int n = 0; n < planstate->sinstrument->num_workers; n++)
3953 {
3955
3956 if (si->exact_pages == 0 && si->lossy_pages == 0)
3957 continue;
3958
3959 if (es->workers_state)
3960 ExplainOpenWorker(n, es);
3961
3962 if (es->format == EXPLAIN_FORMAT_TEXT)
3963 {
3965 appendStringInfoString(es->str, "Heap Blocks:");
3966 if (si->exact_pages > 0)
3967 appendStringInfo(es->str, " exact=" UINT64_FORMAT, si->exact_pages);
3968 if (si->lossy_pages > 0)
3969 appendStringInfo(es->str, " lossy=" UINT64_FORMAT, si->lossy_pages);
3970 appendStringInfoChar(es->str, '\n');
3971 }
3972 else
3973 {
3974 ExplainPropertyUInteger("Exact Heap Blocks", NULL,
3975 si->exact_pages, es);
3976 ExplainPropertyUInteger("Lossy Heap Blocks", NULL,
3977 si->lossy_pages, es);
3978 }
3979
3980 if (es->workers_state)
3981 ExplainCloseWorker(n, es);
3982 }
3983 }
3984}
ParallelBitmapHeapState * pstate
Definition: execnodes.h:1901
BitmapHeapScanInstrumentation stats
Definition: execnodes.h:1899
SharedBitmapHeapInstrumentation * sinstrument
Definition: execnodes.h:1902
BitmapHeapScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:1879

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 2562 of file explain.c.

2565{
2566 bool useprefix;
2567
2568 useprefix = (es->rtable_size > 1 || es->verbose);
2569 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2570}

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 4282 of file explain.c.

4283{
4284 if (es->format == EXPLAIN_FORMAT_TEXT)
4285 {
4286 /* Show only positive counter values. */
4287 if ((usage->wal_records > 0) || (usage->wal_fpi > 0) ||
4288 (usage->wal_bytes > 0) || (usage->wal_buffers_full > 0) ||
4289 (usage->wal_fpi_bytes > 0))
4290 {
4292 appendStringInfoString(es->str, "WAL:");
4293
4294 if (usage->wal_records > 0)
4295 appendStringInfo(es->str, " records=%" PRId64,
4296 usage->wal_records);
4297 if (usage->wal_fpi > 0)
4298 appendStringInfo(es->str, " fpi=%" PRId64,
4299 usage->wal_fpi);
4300 if (usage->wal_bytes > 0)
4301 appendStringInfo(es->str, " bytes=%" PRIu64,
4302 usage->wal_bytes);
4303 if (usage->wal_fpi_bytes > 0)
4304 appendStringInfo(es->str, " fpi bytes=%" PRIu64,
4305 usage->wal_fpi_bytes);
4306 if (usage->wal_buffers_full > 0)
4307 appendStringInfo(es->str, " buffers full=%" PRId64,
4308 usage->wal_buffers_full);
4309 appendStringInfoChar(es->str, '\n');
4310 }
4311 }
4312 else
4313 {
4314 ExplainPropertyInteger("WAL Records", NULL,
4315 usage->wal_records, es);
4316 ExplainPropertyInteger("WAL FPI", NULL,
4317 usage->wal_fpi, es);
4318 ExplainPropertyUInteger("WAL Bytes", NULL,
4319 usage->wal_bytes, es);
4320 ExplainPropertyUInteger("WAL FPI Bytes", NULL,
4321 usage->wal_fpi_bytes, es);
4322 ExplainPropertyInteger("WAL Buffers Full", NULL,
4323 usage->wal_buffers_full, es);
4324 }
4325}

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

Referenced by ExplainNode().

◆ show_window_def()

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

Definition at line 2896 of file explain.c.

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

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 2959 of file explain.c.

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

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 3498 of file explain.c.

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

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 319 of file explain.c.

323{
325 instr_time planstart,
326 planduration;
327 BufferUsage bufusage_start,
328 bufusage;
329 MemoryContextCounters mem_counters;
330 MemoryContext planner_ctx = NULL;
331 MemoryContext saved_ctx = NULL;
332
333 if (es->memory)
334 {
335 /*
336 * Create a new memory context to measure planner's memory consumption
337 * accurately. Note that if the planner were to be modified to use a
338 * different memory context type, here we would be changing that to
339 * AllocSet, which might be undesirable. However, we don't have a way
340 * to create a context of the same type as another, so we pray and
341 * hope that this is OK.
342 */
344 "explain analyze planner context",
346 saved_ctx = MemoryContextSwitchTo(planner_ctx);
347 }
348
349 if (es->buffers)
350 bufusage_start = pgBufferUsage;
351 INSTR_TIME_SET_CURRENT(planstart);
352
353 /* plan the query */
354 plan = pg_plan_query(query, queryString, cursorOptions, params, es);
355
356 INSTR_TIME_SET_CURRENT(planduration);
357 INSTR_TIME_SUBTRACT(planduration, planstart);
358
359 if (es->memory)
360 {
361 MemoryContextSwitchTo(saved_ctx);
362 MemoryContextMemConsumed(planner_ctx, &mem_counters);
363 }
364
365 /* calc differences of buffer counters. */
366 if (es->buffers)
367 {
368 memset(&bufusage, 0, sizeof(BufferUsage));
369 BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
370 }
371
372 /* run it (if needed) and produce output */
373 ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
374 &planduration, (es->buffers ? &bufusage : NULL),
375 es->memory ? &mem_counters : NULL);
376}
void ExplainOnePlan(PlannedStmt *plannedstmt, 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:245
void MemoryContextMemConsumed(MemoryContext context, MemoryContextCounters *consumed)
Definition: mcxt.c:835
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
#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, ExplainState *es)
Definition: postgres.c:887

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 _PG_init(), and 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 _PG_init(), and ExplainOnePlan().

◆ ExplainOneQuery_hook

ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL

Definition at line 50 of file explain.c.

Referenced by ExplainOneQuery().