PostgreSQL Source Code git master
Loading...
Searching...
No Matches
explain.c File Reference
#include "postgres.h"
#include "access/relscan.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/tuplestore.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_scan_io_usage (ScanState *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 charexplain_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)
 
static void print_io_usage (ExplainState *es, IOStats *stats)
 

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

Function Documentation

◆ elapsed_time()

static double elapsed_time ( instr_time starttime)
static

Definition at line 1171 of file explain.c.

1172{
1174
1176 INSTR_TIME_SUBTRACT(endtime, *starttime);
1178}
#define INSTR_TIME_SET_CURRENT(t)
Definition instr_time.h:426
#define INSTR_TIME_GET_DOUBLE(t)
Definition instr_time.h:448
#define INSTR_TIME_SUBTRACT(x, y)
Definition instr_time.h:436
static int fb(int x)

References fb(), 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 4228 of file explain.c.

4229{
4230 const char *result;
4231
4233 result = (*explain_get_index_name_hook) (indexId);
4234 else
4235 result = NULL;
4236 if (result == NULL)
4237 {
4238 /* default behavior: look it up in the catalogs */
4240 if (result == NULL)
4241 elog(ERROR, "cache lookup failed for index %u", indexId);
4242 }
4243 return result;
4244}
uint32 result
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:227
explain_get_index_name_hook_type explain_get_index_name_hook
Definition explain.c:55
char * get_rel_name(Oid relid)
Definition lsyscache.c:2148

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

Referenced by ExplainIndexScanDetails(), and ExplainNode().

◆ ExplainCloseWorker()

static void ExplainCloseWorker ( int  n,
ExplainState es 
)
static

Definition at line 5265 of file explain.c.

5266{
5268
5269 Assert(wstate);
5270 Assert(n >= 0 && n < wstate->num_workers);
5271 Assert(wstate->worker_inited[n]);
5272
5273 /*
5274 * Save formatting state in case we do another ExplainOpenWorker(), then
5275 * pop the formatting stack.
5276 */
5277 ExplainSaveGroup(es, 2, &wstate->worker_state_save[n]);
5278
5279 /*
5280 * In TEXT format, if we didn't actually produce any output line(s) then
5281 * truncate off the partial line emitted by ExplainOpenWorker. (This is
5282 * to avoid bogus output if, say, show_buffer_usage chooses not to print
5283 * anything for the worker.) Also fix up the indent level.
5284 */
5285 if (es->format == EXPLAIN_FORMAT_TEXT)
5286 {
5287 while (es->str->len > 0 && es->str->data[es->str->len - 1] != '\n')
5288 es->str->data[--(es->str->len)] = '\0';
5289
5290 es->indent--;
5291 }
5292
5293 /* Restore prior output buffer pointer */
5294 es->str = wstate->prev_str;
5295}
#define Assert(condition)
Definition c.h:943
void ExplainSaveGroup(ExplainState *es, int depth, int *state_save)
@ EXPLAIN_FORMAT_TEXT
ExplainWorkersState * workers_state
StringInfo str
ExplainFormat format

References Assert, StringInfoData::data, EXPLAIN_FORMAT_TEXT, ExplainSaveGroup(), fb(), ExplainState::format, ExplainState::indent, StringInfoData::len, ExplainState::str, and ExplainState::workers_state.

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

◆ ExplainCreateWorkersState()

static ExplainWorkersState * ExplainCreateWorkersState ( int  num_workers)
static

Definition at line 5186 of file explain.c.

5187{
5189
5191 wstate->num_workers = num_workers;
5192 wstate->worker_inited = (bool *) palloc0(num_workers * sizeof(bool));
5193 wstate->worker_str = (StringInfoData *)
5194 palloc0(num_workers * sizeof(StringInfoData));
5195 wstate->worker_state_save = (int *) palloc(num_workers * sizeof(int));
5196 return wstate;
5197}
#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

References fb(), palloc(), palloc0(), and palloc_object.

Referenced by ExplainNode().

◆ ExplainCustomChildren()

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

Definition at line 5164 of file explain.c.

5165{
5166 ListCell *cell;
5167 const char *label =
5168 (list_length(css->custom_ps) != 1 ? "children" : "child");
5169
5170 foreach(cell, css->custom_ps)
5171 ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
5172}
static void ExplainNode(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
Definition explain.c:1363
static char * label
#define lfirst(lc)
Definition pg_list.h:172
static int list_length(const List *l)
Definition pg_list.h:152

References ExplainNode(), fb(), label, lfirst, and list_length().

Referenced by ExplainNode().

◆ ExplainFlushWorkersState()

static void ExplainFlushWorkersState ( ExplainState es)
static

Definition at line 5301 of file explain.c.

5302{
5304
5305 ExplainOpenGroup("Workers", "Workers", false, es);
5306 for (int i = 0; i < wstate->num_workers; i++)
5307 {
5308 if (wstate->worker_inited[i])
5309 {
5310 /* This must match previous ExplainOpenSetAsideGroup call */
5311 ExplainOpenGroup("Worker", NULL, true, es);
5312 appendStringInfoString(es->str, wstate->worker_str[i].data);
5313 ExplainCloseGroup("Worker", NULL, true, es);
5314
5315 pfree(wstate->worker_str[i].data);
5316 }
5317 }
5318 ExplainCloseGroup("Workers", "Workers", false, es);
5319
5320 pfree(wstate->worker_inited);
5321 pfree(wstate->worker_str);
5322 pfree(wstate->worker_state_save);
5323 pfree(wstate);
5324}
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(), fb(), i, pfree(), ExplainState::str, and ExplainState::workers_state.

Referenced by ExplainNode().

◆ ExplainIndexScanDetails()

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

Definition at line 4536 of file explain.c.

4538{
4539 const char *indexname = explain_get_index_name(indexid);
4540
4541 if (es->format == EXPLAIN_FORMAT_TEXT)
4542 {
4543 if (ScanDirectionIsBackward(indexorderdir))
4544 appendStringInfoString(es->str, " Backward");
4545 appendStringInfo(es->str, " using %s", quote_identifier(indexname));
4546 }
4547 else
4548 {
4549 const char *scandir;
4550
4551 switch (indexorderdir)
4552 {
4554 scandir = "Backward";
4555 break;
4557 scandir = "Forward";
4558 break;
4559 default:
4560 scandir = "???";
4561 break;
4562 }
4563 ExplainPropertyText("Scan Direction", scandir, es);
4564 ExplainPropertyText("Index Name", indexname, es);
4565 }
4566}
static const char * explain_get_index_name(Oid indexId)
Definition explain.c:4228
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
const char * quote_identifier(const char *ident)
#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(), fb(), 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 5076 of file explain.c.

5078{
5079 int j;
5080
5081 for (j = 0; j < nplans; j++)
5082 ExplainNode(planstates[j], ancestors,
5083 "Member", NULL, es);
5084}
int j
Definition isn.c:78

References ExplainNode(), fb(), and j.

Referenced by ExplainNode().

◆ ExplainMissingMembers()

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

Definition at line 5094 of file explain.c.

5095{
5097 ExplainPropertyInteger("Subplans Removed", NULL,
5098 nchildren - nplans, es);
5099}
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
static char format

References EXPLAIN_FORMAT_TEXT, ExplainPropertyInteger(), fb(), and format.

Referenced by ExplainNode().

◆ ExplainModifyTarget()

static void ExplainModifyTarget ( ModifyTable plan,
ExplainState es 
)
static

Definition at line 4585 of file explain.c.

4586{
4587 ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
4588}
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
Definition explain.c:4594
#define plan(x)
Definition pg_regress.c:164

References ExplainTargetRel(), and plan.

Referenced by ExplainNode().

◆ ExplainNode()

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

Definition at line 1363 of file explain.c.

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

References AGG_HASHED, AGG_MIXED, AGG_PLAIN, AGG_SORTED, 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(), ExplainFlushWorkersState(), ExplainIndentText(), ExplainIndexScanDetails(), ExplainMemberNodes(), ExplainMissingMembers(), ExplainModifyTarget(), ExplainNode(), ExplainOpenGroup(), ExplainOpenWorker(), ExplainPrintJIT(), ExplainPropertyBool(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyText(), ExplainScanTarget(), ExplainSubPlans(), fb(), ExplainState::format, RangeTblFunction::funcexpr, functions, ExplainState::hide_workers, ExplainState::indent, PlanState::initPlan, innerPlanState, NodeInstrumentation::instr, INSTR_TIME_GET_MILLISEC, InstrEndLoop(), WorkerNodeInstrumentation::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(), NIL, NodeInstrumentation::nloops, nodeTag, NodeInstrumentation::ntuples, NodeInstrumentation::ntuples2, WorkerNodeInstrumentation::num_workers, SharedJitInstrumentation::num_workers, operation, outerPlanState, pg_fallthrough, 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_io_usage(), 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(), NodeInstrumentation::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 500 of file explain.c.

505{
507 QueryDesc *queryDesc;
508 instr_time starttime;
509 double totaltime = 0;
510 int eflags;
511 int instrument_option = 0;
513
514 Assert(plannedstmt->commandType != CMD_UTILITY);
515
516 if (es->analyze && es->timing)
518 else if (es->analyze)
520
521 if (es->buffers)
523 if (es->wal)
525 if (es->io)
527
528 /*
529 * We always collect timing for the entire statement, even when node-level
530 * timing is off, so we don't look at es->timing here. (We could skip
531 * this if !es->summary, but it's hardly worth the complication.)
532 */
533 INSTR_TIME_SET_CURRENT(starttime);
534
535 /*
536 * Use a snapshot with an updated command ID to ensure this query sees
537 * results of any previously executed queries.
538 */
541
542 /*
543 * We discard the output if we have no use for it. If we're explaining
544 * CREATE TABLE AS, we'd better use the appropriate tuple receiver, while
545 * the SERIALIZE option requires its own tuple receiver. (If you specify
546 * SERIALIZE while explaining CREATE TABLE AS, you'll see zeroes for the
547 * results, which is appropriate since no data would have gone to the
548 * client.)
549 */
550 if (into)
552 else if (es->serialize != EXPLAIN_SERIALIZE_NONE)
554 else
556
557 /* Create a QueryDesc for the query */
558 queryDesc = CreateQueryDesc(plannedstmt, queryString,
560 dest, params, queryEnv, instrument_option);
561
562 /* Select execution options */
563 if (es->analyze)
564 eflags = 0; /* default run-to-completion flags */
565 else
566 eflags = EXEC_FLAG_EXPLAIN_ONLY;
567 if (es->generic)
569 if (into)
570 eflags |= GetIntoRelEFlags(into);
571
572 /* call ExecutorStart to prepare the plan for execution */
573 ExecutorStart(queryDesc, eflags);
574
575 /* Execute the plan for statistics if asked for */
576 if (es->analyze)
577 {
578 ScanDirection dir;
579
580 /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
581 if (into && into->skipData)
583 else
585
586 /* run the plan */
587 ExecutorRun(queryDesc, dir, 0);
588
589 /* run cleanup too */
590 ExecutorFinish(queryDesc);
591
592 /* We can't run ExecutorEnd 'till we're done printing the stats... */
593 totaltime += elapsed_time(&starttime);
594 }
595
596 /* grab serialization metrics before we destroy the DestReceiver */
599
600 /* call the DestReceiver's destroy method even during explain */
601 dest->rDestroy(dest);
602
603 ExplainOpenGroup("Query", NULL, true, es);
604
605 /* Create textual dump of plan tree */
606 ExplainPrintPlan(es, queryDesc);
607
608 /* Show buffer and/or memory usage in planning */
609 if (peek_buffer_usage(es, bufusage) || mem_counters)
610 {
611 ExplainOpenGroup("Planning", "Planning", true, es);
612
613 if (es->format == EXPLAIN_FORMAT_TEXT)
614 {
616 appendStringInfoString(es->str, "Planning:\n");
617 es->indent++;
618 }
619
620 if (bufusage)
621 show_buffer_usage(es, bufusage);
622
623 if (mem_counters)
625
626 if (es->format == EXPLAIN_FORMAT_TEXT)
627 es->indent--;
628
629 ExplainCloseGroup("Planning", "Planning", true, es);
630 }
631
632 if (es->summary && planduration)
633 {
635
636 ExplainPropertyFloat("Planning Time", "ms", 1000.0 * plantime, 3, es);
637 }
638
639 /* Print info about runtime of triggers */
640 if (es->analyze)
641 ExplainPrintTriggers(es, queryDesc);
642
643 /*
644 * Print info about JITing. Tied to es->costs because we don't want to
645 * display this in regression tests, as it'd cause output differences
646 * depending on build options. Might want to separate that out from COSTS
647 * at a later stage.
648 */
649 if (es->costs)
650 ExplainPrintJITSummary(es, queryDesc);
651
652 /* Print info about serialization of output */
655
656 /* Allow plugins to print additional information */
658 (*explain_per_plan_hook) (plannedstmt, into, es, queryString,
659 params, queryEnv);
660
661 /*
662 * Close down the query and free resources. Include time for this in the
663 * total execution time (although it should be pretty minimal).
664 */
665 INSTR_TIME_SET_CURRENT(starttime);
666
667 ExecutorEnd(queryDesc);
668
669 FreeQueryDesc(queryDesc);
670
672
673 /* We need a CCI just in case query expanded to multiple plans */
674 if (es->analyze)
676
677 totaltime += elapsed_time(&starttime);
678
679 /*
680 * We only report execution time if we actually ran the query (that is,
681 * the user specified ANALYZE), and if summary reporting is enabled (the
682 * user can set SUMMARY OFF to not have the timing information included in
683 * the output). By default, ANALYZE sets SUMMARY to true.
684 */
685 if (es->summary && es->analyze)
686 ExplainPropertyFloat("Execution Time", "ms", 1000.0 * totaltime, 3,
687 es);
688
689 ExplainCloseGroup("Query", NULL, true, es);
690}
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:477
void ExecutorFinish(QueryDesc *queryDesc)
Definition execMain.c:417
void ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition execMain.c:124
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
Definition execMain.c:308
#define EXEC_FLAG_EXPLAIN_GENERIC
Definition executor.h:68
#define EXEC_FLAG_EXPLAIN_ONLY
Definition executor.h:67
static bool peek_buffer_usage(ExplainState *es, const BufferUsage *usage)
Definition explain.c:4252
void ExplainPrintJITSummary(ExplainState *es, QueryDesc *queryDesc)
Definition explain.c:883
static double elapsed_time(instr_time *starttime)
Definition explain.c:1171
explain_per_plan_hook_type explain_per_plan_hook
Definition explain.c:58
static void show_memory_counters(ExplainState *es, const MemoryContextCounters *mem_counters)
Definition explain.c:4510
void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
Definition explain.c:767
static void ExplainPrintSerialize(ExplainState *es, SerializeMetrics *metrics)
Definition explain.c:1007
void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
Definition explain.c:840
SerializeMetrics GetSerializationMetrics(DestReceiver *dest)
Definition explain_dr.c:300
DestReceiver * CreateExplainSerializeDestReceiver(ExplainState *es)
Definition explain_dr.c:275
@ EXPLAIN_SERIALIZE_NONE
@ INSTRUMENT_TIMER
Definition instrument.h:63
@ INSTRUMENT_IO
Definition instrument.h:67
@ 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:107
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
bool skipData
Definition primnodes.h:172
CmdType commandType
Definition plannodes.h:66
void CommandCounterIncrement(void)
Definition xact.c:1130

References ExplainState::analyze, appendStringInfoString(), Assert, ExplainState::buffers, CMD_UTILITY, CommandCounterIncrement(), PlannedStmt::commandType, ExplainState::costs, CreateExplainSerializeDestReceiver(), CreateIntoRelDestReceiver(), CreateQueryDesc(), 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(), fb(), ExplainState::format, ForwardScanDirection, FreeQueryDesc(), ExplainState::generic, GetActiveSnapshot(), GetIntoRelEFlags(), GetSerializationMetrics(), ExplainState::indent, INSTR_TIME_GET_DOUBLE, INSTR_TIME_SET_CURRENT, INSTRUMENT_BUFFERS, INSTRUMENT_IO, INSTRUMENT_ROWS, INSTRUMENT_TIMER, INSTRUMENT_WAL, InvalidSnapshot, ExplainState::io, 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 299 of file explain.c.

302{
303 /* planner will not cope with utility statements */
304 if (query->commandType == CMD_UTILITY)
305 {
306 ExplainOneUtility(query->utilityStmt, into, es, pstate, params);
307 return;
308 }
309
310 /* if an advisor plugin is present, let it manage things */
312 (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
313 pstate->p_sourcetext, params, pstate->p_queryEnv);
314 else
315 standard_ExplainOneQuery(query, cursorOptions, into, es,
316 pstate->p_sourcetext, params, pstate->p_queryEnv);
317}
ExplainOneQuery_hook_type ExplainOneQuery_hook
Definition explain.c:52
void ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
Definition explain.c:396
void standard_ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition explain.c:324
QueryEnvironment * p_queryEnv
Definition parse_node.h:241
const char * p_sourcetext
Definition parse_node.h:214
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 396 of file explain.c.

398{
399 if (utilityStmt == NULL)
400 return;
401
402 if (IsA(utilityStmt, CreateTableAsStmt))
403 {
404 /*
405 * We have to rewrite the contained SELECT and then pass it back to
406 * ExplainOneQuery. Copy to be safe in the EXPLAIN EXECUTE case.
407 */
408 CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
412
413 /*
414 * Check if the relation exists or not. This is done at this stage to
415 * avoid query planning or execution.
416 */
418 {
419 if (ctas->objtype == OBJECT_TABLE)
420 ExplainDummyGroup("CREATE TABLE AS", NULL, es);
421 else if (ctas->objtype == OBJECT_MATVIEW)
422 ExplainDummyGroup("CREATE MATERIALIZED VIEW", NULL, es);
423 else
424 elog(ERROR, "unexpected object type: %d",
425 (int) ctas->objtype);
426 return;
427 }
428
430 if (IsQueryIdEnabled())
433 (*post_parse_analyze_hook) (pstate, ctas_query, jstate);
437 CURSOR_OPT_PARALLEL_OK, ctas->into, es,
438 pstate, params);
439 }
440 else if (IsA(utilityStmt, DeclareCursorStmt))
441 {
442 /*
443 * Likewise for DECLARE CURSOR.
444 *
445 * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
446 * actually run the query. This is different from pre-8.3 behavior
447 * but seems more useful than not running the query. No cursor will
448 * be created, however.
449 */
450 DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
454
456 if (IsQueryIdEnabled())
459 (*post_parse_analyze_hook) (pstate, dcs_query, jstate);
460
464 dcs->options, NULL, es,
465 pstate, params);
466 }
467 else if (IsA(utilityStmt, ExecuteStmt))
468 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
469 pstate, params);
470 else if (IsA(utilityStmt, NotifyStmt))
471 {
472 if (es->format == EXPLAIN_FORMAT_TEXT)
473 appendStringInfoString(es->str, "NOTIFY\n");
474 else
475 ExplainDummyGroup("Notify", NULL, es);
476 }
477 else
478 {
479 if (es->format == EXPLAIN_FORMAT_TEXT)
481 "Utility statements have no plan structure\n");
482 else
483 ExplainDummyGroup("Utility Statement", NULL, es);
484 }
485}
void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
Definition prepare.c:573
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:299
void ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
#define copyObject(obj)
Definition nodes.h:232
@ OBJECT_MATVIEW
@ OBJECT_TABLE
#define CURSOR_OPT_PARALLEL_OK
post_parse_analyze_hook_type post_parse_analyze_hook
Definition analyze.c:74
#define linitial_node(type, l)
Definition pg_list.h:181
static bool IsQueryIdEnabled(void)
JumbleState * JumbleQuery(Query *query)
List * QueryRewrite(Query *parsetree)

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

Referenced by ExplainExecuteQuery(), and ExplainOneQuery().

◆ ExplainOpenWorker()

static void ExplainOpenWorker ( int  n,
ExplainState es 
)
static

Definition at line 5203 of file explain.c.

5204{
5206
5207 Assert(wstate);
5208 Assert(n >= 0 && n < wstate->num_workers);
5209
5210 /* Save prior output buffer pointer */
5211 wstate->prev_str = es->str;
5212
5213 if (!wstate->worker_inited[n])
5214 {
5215 /* First time through, so create the buffer for this worker */
5216 initStringInfo(&wstate->worker_str[n]);
5217 es->str = &wstate->worker_str[n];
5218
5219 /*
5220 * Push suitable initial formatting state for this worker's field
5221 * group. We allow one extra logical nesting level, since this group
5222 * will eventually be wrapped in an outer "Workers" group.
5223 */
5224 ExplainOpenSetAsideGroup("Worker", NULL, true, 2, es);
5225
5226 /*
5227 * In non-TEXT formats we always emit a "Worker Number" field, even if
5228 * there's no other data for this worker.
5229 */
5230 if (es->format != EXPLAIN_FORMAT_TEXT)
5231 ExplainPropertyInteger("Worker Number", NULL, n, es);
5232
5233 wstate->worker_inited[n] = true;
5234 }
5235 else
5236 {
5237 /* Resuming output for a worker we've already emitted some data for */
5238 es->str = &wstate->worker_str[n];
5239
5240 /* Restore formatting state saved by last ExplainCloseWorker() */
5241 ExplainRestoreGroup(es, 2, &wstate->worker_state_save[n]);
5242 }
5243
5244 /*
5245 * In TEXT format, prefix the first output line for this worker with
5246 * "Worker N:". Then, any additional lines should be indented one more
5247 * stop than the "Worker N" line is.
5248 */
5249 if (es->format == EXPLAIN_FORMAT_TEXT)
5250 {
5251 if (es->str->len == 0)
5252 {
5254 appendStringInfo(es->str, "Worker %d: ", n);
5255 }
5256
5257 es->indent++;
5258 }
5259}
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(), fb(), ExplainState::format, ExplainState::indent, initStringInfo(), StringInfoData::len, ExplainState::str, and ExplainState::workers_state.

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

◆ ExplainPreScanNode()

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

Definition at line 1190 of file explain.c.

1191{
1192 Plan *plan = planstate->plan;
1193
1194 switch (nodeTag(plan))
1195 {
1196 case T_SeqScan:
1197 case T_SampleScan:
1198 case T_IndexScan:
1199 case T_IndexOnlyScan:
1200 case T_BitmapHeapScan:
1201 case T_TidScan:
1202 case T_TidRangeScan:
1203 case T_SubqueryScan:
1204 case T_FunctionScan:
1205 case T_TableFuncScan:
1206 case T_ValuesScan:
1207 case T_CteScan:
1209 case T_WorkTableScan:
1211 ((Scan *) plan)->scanrelid);
1212 break;
1213 case T_ForeignScan:
1215 ((ForeignScan *) plan)->fs_base_relids);
1216 break;
1217 case T_CustomScan:
1219 ((CustomScan *) plan)->custom_relids);
1220 break;
1221 case T_ModifyTable:
1223 ((ModifyTable *) plan)->nominalRelation);
1224 if (((ModifyTable *) plan)->exclRelRTI)
1226 ((ModifyTable *) plan)->exclRelRTI);
1227 /* Ensure Vars used in RETURNING will have refnames */
1228 if (plan->targetlist)
1230 linitial_int(((ModifyTable *) plan)->resultRelations));
1231 break;
1232 case T_Append:
1234 ((Append *) plan)->apprelids);
1235 break;
1236 case T_MergeAppend:
1238 ((MergeAppend *) plan)->apprelids);
1239 break;
1240 case T_Result:
1242 ((Result *) plan)->relids);
1243 break;
1244 default:
1245 break;
1246 }
1247
1249}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:901
static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
Definition explain.c:1190
#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(), fb(), 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 909 of file explain.c.

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

References ExplainState::analyze, appendStringInfo(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainIndentText(), ExplainOpenGroup(), ExplainPropertyBool(), ExplainPropertyFloat(), ExplainPropertyInteger(), fb(), ExplainState::format, ExplainState::indent, INSTR_TIME_ADD, INSTR_TIME_GET_DOUBLE, INSTR_TIME_SET_ZERO, 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 883 of file explain.c.

884{
886
887 if (!(queryDesc->estate->es_jit_flags & PGJIT_PERFORM))
888 return;
889
890 /*
891 * Work with a copy instead of modifying the leader state, since this
892 * function may be called twice
893 */
894 if (queryDesc->estate->es_jit)
895 InstrJitAgg(&ji, &queryDesc->estate->es_jit->instr);
896
897 /* If this process has done JIT in parallel workers, merge stats */
898 if (queryDesc->estate->es_jit_worker_instr)
900
901 ExplainPrintJIT(es, queryDesc->estate->es_jit_flags, &ji);
902}
void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
Definition jit.c:183
#define PGJIT_PERFORM
Definition jit.h:20
struct JitContext * es_jit
Definition execnodes.h:800
struct JitInstrumentation * es_jit_worker_instr
Definition execnodes.h:801
JitInstrumentation instr
Definition jit.h:62
EState * estate
Definition execdesc.h:50

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

Referenced by explain_ExecutorEnd(), and ExplainOnePlan().

◆ ExplainPrintPlan()

void ExplainPrintPlan ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 767 of file explain.c.

768{
770 PlanState *ps;
771 ListCell *lc;
772
773 /* Set up ExplainState fields associated with this plan tree */
774 Assert(queryDesc->plannedstmt != NULL);
775 es->pstmt = queryDesc->plannedstmt;
776 es->rtable = queryDesc->plannedstmt->rtable;
780 es->rtable_names);
782 es->rtable_size = list_length(es->rtable);
783 foreach(lc, es->rtable)
784 {
786
787 if (rte->rtekind == RTE_GROUP)
788 {
789 es->rtable_size--;
790 break;
791 }
792 }
793
794 /*
795 * Sometimes we mark a Gather node as "invisible", which means that it's
796 * not to be displayed in EXPLAIN output. The purpose of this is to allow
797 * running regression tests with debug_parallel_query=regress to get the
798 * same results as running the same tests with debug_parallel_query=off.
799 * Such marking is currently only supported on a Gather at the top of the
800 * plan. We skip that node, and we must also hide per-worker detail data
801 * further down in the plan tree.
802 */
803 ps = queryDesc->planstate;
804 if (IsA(ps, GatherState) && ((Gather *) ps->plan)->invisible)
805 {
807 es->hide_workers = true;
808 }
809 ExplainNode(ps, NIL, NULL, NULL, es);
810
811 /*
812 * If requested, include information about GUC parameters with values that
813 * don't match the built-in defaults.
814 */
816
817 /*
818 * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_AUTO, but we don't show
819 * the queryid in any of the EXPLAIN plans to keep stable the results
820 * generated by regression test suites.
821 */
822 if (es->verbose && queryDesc->plannedstmt->queryId != INT64CONST(0) &&
824 {
825 ExplainPropertyInteger("Query Identifier", NULL,
826 queryDesc->plannedstmt->queryId, es);
827 }
828}
#define INT64CONST(x)
Definition c.h:630
static void ExplainPrintSettings(ExplainState *es)
Definition explain.c:697
struct parser_state ps
@ RTE_GROUP
#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:4115
List * select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
Definition ruleutils.c:4217
Bitmapset * printed_subplans
List * rtable_names
PlannedStmt * pstmt
List * deparse_cxt
int64 queryId
Definition plannodes.h:69
List * rtable
Definition plannodes.h:107
PlannedStmt * plannedstmt
Definition execdesc.h:37
PlanState * planstate
Definition execdesc.h:51

References Assert, compute_query_id, COMPUTE_QUERY_ID_REGRESS, deparse_context_for_plan_tree(), ExplainState::deparse_cxt, ExplainNode(), ExplainPreScanNode(), ExplainPrintSettings(), ExplainPropertyInteger(), fb(), 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, 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 1007 of file explain.c.

1008{
1009 const char *format;
1010
1011 /* We shouldn't get called for EXPLAIN_SERIALIZE_NONE */
1013 format = "text";
1014 else
1015 {
1017 format = "binary";
1018 }
1019
1020 ExplainOpenGroup("Serialization", "Serialization", true, es);
1021
1022 if (es->format == EXPLAIN_FORMAT_TEXT)
1023 {
1025 if (es->timing)
1026 appendStringInfo(es->str, "Serialization: time=%.3f ms output=" UINT64_FORMAT "kB format=%s\n",
1027 1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
1028 BYTES_TO_KILOBYTES(metrics->bytesSent),
1029 format);
1030 else
1031 appendStringInfo(es->str, "Serialization: output=" UINT64_FORMAT "kB format=%s\n",
1032 BYTES_TO_KILOBYTES(metrics->bytesSent),
1033 format);
1034
1035 if (es->buffers && peek_buffer_usage(es, &metrics->bufferUsage))
1036 {
1037 es->indent++;
1038 show_buffer_usage(es, &metrics->bufferUsage);
1039 es->indent--;
1040 }
1041 }
1042 else
1043 {
1044 if (es->timing)
1045 ExplainPropertyFloat("Time", "ms",
1046 1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
1047 3, es);
1048 ExplainPropertyUInteger("Output Volume", "kB",
1049 BYTES_TO_KILOBYTES(metrics->bytesSent), es);
1050 ExplainPropertyText("Format", format, es);
1051 if (es->buffers)
1052 show_buffer_usage(es, &metrics->bufferUsage);
1053 }
1054
1055 ExplainCloseGroup("Serialization", "Serialization", true, es);
1056}
#define UINT64_FORMAT
Definition c.h:635
#define BYTES_TO_KILOBYTES(b)
Definition explain.c:65
void ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value, ExplainState *es)
@ EXPLAIN_SERIALIZE_TEXT
@ EXPLAIN_SERIALIZE_BINARY
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 697 of file explain.c.

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

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

Referenced by ExplainPrintPlan().

◆ ExplainPrintTriggers()

void ExplainPrintTriggers ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 840 of file explain.c.

841{
843 bool show_relname;
846 List *targrels;
847 ListCell *l;
848
852
853 ExplainOpenGroup("Triggers", "Triggers", false, es);
854
856 routerels != NIL || targrels != NIL);
857 foreach(l, resultrels)
858 {
859 rInfo = (ResultRelInfo *) lfirst(l);
861 }
862
863 foreach(l, routerels)
864 {
865 rInfo = (ResultRelInfo *) lfirst(l);
867 }
868
869 foreach(l, targrels)
870 {
871 rInfo = (ResultRelInfo *) lfirst(l);
873 }
874
875 ExplainCloseGroup("Triggers", "Triggers", false, es);
876}
static void report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
Definition explain.c:1100
List * es_tuple_routing_result_relations
Definition execnodes.h:734
List * es_trig_target_relations
Definition execnodes.h:737
List * es_opened_result_relations
Definition execnodes.h:724

References EState::es_opened_result_relations, EState::es_trig_target_relations, EState::es_tuple_routing_result_relations, QueryDesc::estate, ExplainCloseGroup(), ExplainOpenGroup(), fb(), 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 181 of file explain.c.

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

References appendStringInfoString(), Assert, begin_tup_output_tupdesc(), castNode, CURSOR_OPT_PARALLEL_OK, StringInfoData::data, do_text_output_multiline(), do_text_output_oneline, end_tup_output(), EXPLAIN_FORMAT_TEXT, ExplainBeginOutput(), ExplainEndOutput(), ExplainOneQuery(), ExplainResultDesc(), ExplainSeparatePlans(), fb(), 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 1082 of file explain.c.

1083{
1084 char *str;
1085
1086 /* This check is consistent with errdetail_params() */
1087 if (params == NULL || params->numParams <= 0 || maxlen == 0)
1088 return;
1089
1090 str = BuildParamLogString(params, NULL, maxlen);
1091 if (str && str[0] != '\0')
1092 ExplainPropertyText("Query Parameters", str, es);
1093}
char * BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen)
Definition params.c:333

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

Referenced by explain_ExecutorEnd().

◆ ExplainQueryText()

void ExplainQueryText ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 1067 of file explain.c.

1068{
1069 if (queryDesc->sourceText)
1070 ExplainPropertyText("Query Text", queryDesc->sourceText, es);
1071}
const char * sourceText
Definition execdesc.h:38

References ExplainPropertyText(), and QueryDesc::sourceText.

Referenced by explain_ExecutorEnd().

◆ ExplainResultDesc()

TupleDesc ExplainResultDesc ( ExplainStmt stmt)

Definition at line 259 of file explain.c.

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

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

Referenced by ExplainQuery(), and UtilityTupleDescriptor().

◆ ExplainScanTarget()

static void ExplainScanTarget ( Scan plan,
ExplainState es 
)
static

Definition at line 4572 of file explain.c.

4573{
4574 ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
4575}

References ExplainTargetRel(), and plan.

Referenced by ExplainNode().

◆ ExplainSubPlans()

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

Definition at line 5108 of file explain.c.

5110{
5111 ListCell *lst;
5112
5113 foreach(lst, plans)
5114 {
5116 SubPlan *sp = sps->subplan;
5117 char *cooked_plan_name;
5118
5119 /*
5120 * There can be multiple SubPlan nodes referencing the same physical
5121 * subplan (same plan_id, which is its index in PlannedStmt.subplans).
5122 * We should print a subplan only once, so track which ones we already
5123 * printed. This state must be global across the plan tree, since the
5124 * duplicate nodes could be in different plan nodes, eg both a bitmap
5125 * indexscan's indexqual and its parent heapscan's recheck qual. (We
5126 * do not worry too much about which plan node we show the subplan as
5127 * attached to in such cases.)
5128 */
5129 if (bms_is_member(sp->plan_id, es->printed_subplans))
5130 continue;
5132 sp->plan_id);
5133
5134 /*
5135 * Treat the SubPlan node as an ancestor of the plan node(s) within
5136 * it, so that ruleutils.c can find the referents of subplan
5137 * parameters.
5138 */
5139 ancestors = lcons(sp, ancestors);
5140
5141 /*
5142 * The plan has a name like exists_1 or rowcompare_2, but here we want
5143 * to prefix that with CTE, InitPlan, or SubPlan, as appropriate, for
5144 * display purposes.
5145 */
5146 if (sp->subLinkType == CTE_SUBLINK)
5147 cooked_plan_name = psprintf("CTE %s", sp->plan_name);
5148 else if (sp->isInitPlan)
5149 cooked_plan_name = psprintf("InitPlan %s", sp->plan_name);
5150 else
5151 cooked_plan_name = psprintf("SubPlan %s", sp->plan_name);
5152
5153 ExplainNode(sps->planstate, ancestors,
5155
5156 ancestors = list_delete_first(ancestors);
5157 }
5158}
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
@ CTE_SUBLINK
Definition primnodes.h:1037

References bms_add_member(), bms_is_member(), CTE_SUBLINK, ExplainNode(), fb(), lcons(), lfirst, list_delete_first(), ExplainState::printed_subplans, and psprintf().

Referenced by ExplainNode().

◆ ExplainTargetRel()

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

Definition at line 4594 of file explain.c.

4595{
4596 char *objectname = NULL;
4597 char *namespace = NULL;
4598 const char *objecttag = NULL;
4600 char *refname;
4601
4602 rte = rt_fetch(rti, es->rtable);
4603 refname = (char *) list_nth(es->rtable_names, rti - 1);
4604 if (refname == NULL)
4605 refname = rte->eref->aliasname;
4606
4607 switch (nodeTag(plan))
4608 {
4609 case T_SeqScan:
4610 case T_SampleScan:
4611 case T_IndexScan:
4612 case T_IndexOnlyScan:
4613 case T_BitmapHeapScan:
4614 case T_TidScan:
4615 case T_TidRangeScan:
4616 case T_ForeignScan:
4617 case T_CustomScan:
4618 case T_ModifyTable:
4619 /* Assert it's on a real relation */
4620 Assert(rte->rtekind == RTE_RELATION);
4621 objectname = get_rel_name(rte->relid);
4622 if (es->verbose)
4624 objecttag = "Relation Name";
4625 break;
4626 case T_FunctionScan:
4627 {
4629
4630 /* Assert it's on a RangeFunction */
4631 Assert(rte->rtekind == RTE_FUNCTION);
4632
4633 /*
4634 * If the expression is still a function call of a single
4635 * function, we can get the real name of the function.
4636 * Otherwise, punt. (Even if it was a single function call
4637 * originally, the optimizer could have simplified it away.)
4638 */
4639 if (list_length(fscan->functions) == 1)
4640 {
4641 RangeTblFunction *rtfunc = (RangeTblFunction *) linitial(fscan->functions);
4642
4643 if (IsA(rtfunc->funcexpr, FuncExpr))
4644 {
4645 FuncExpr *funcexpr = (FuncExpr *) rtfunc->funcexpr;
4646 Oid funcid = funcexpr->funcid;
4647
4648 objectname = get_func_name(funcid);
4649 if (es->verbose)
4651 }
4652 }
4653 objecttag = "Function Name";
4654 }
4655 break;
4656 case T_TableFuncScan:
4657 {
4658 TableFunc *tablefunc = ((TableFuncScan *) plan)->tablefunc;
4659
4660 Assert(rte->rtekind == RTE_TABLEFUNC);
4661 switch (tablefunc->functype)
4662 {
4663 case TFT_XMLTABLE:
4664 objectname = "xmltable";
4665 break;
4666 case TFT_JSON_TABLE:
4667 objectname = "json_table";
4668 break;
4669 default:
4670 elog(ERROR, "invalid TableFunc type %d",
4671 (int) tablefunc->functype);
4672 }
4673 objecttag = "Table Function Name";
4674 }
4675 break;
4676 case T_ValuesScan:
4677 Assert(rte->rtekind == RTE_VALUES);
4678 break;
4679 case T_CteScan:
4680 /* Assert it's on a non-self-reference CTE */
4681 Assert(rte->rtekind == RTE_CTE);
4682 Assert(!rte->self_reference);
4683 objectname = rte->ctename;
4684 objecttag = "CTE Name";
4685 break;
4687 Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
4688 objectname = rte->enrname;
4689 objecttag = "Tuplestore Name";
4690 break;
4691 case T_WorkTableScan:
4692 /* Assert it's on a self-reference CTE */
4693 Assert(rte->rtekind == RTE_CTE);
4694 Assert(rte->self_reference);
4695 objectname = rte->ctename;
4696 objecttag = "CTE Name";
4697 break;
4698 default:
4699 break;
4700 }
4701
4702 if (es->format == EXPLAIN_FORMAT_TEXT)
4703 {
4704 appendStringInfoString(es->str, " on");
4705 if (namespace != NULL)
4706 appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
4708 else if (objectname != NULL)
4710 if (objectname == NULL || strcmp(refname, objectname) != 0)
4711 appendStringInfo(es->str, " %s", quote_identifier(refname));
4712 }
4713 else
4714 {
4715 if (objecttag != NULL && objectname != NULL)
4717 if (namespace != NULL)
4718 ExplainPropertyText("Schema", namespace, es);
4719 ExplainPropertyText("Alias", refname, es);
4720 }
4721}
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2172
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3612
char * get_func_name(Oid funcid)
Definition lsyscache.c:1828
Oid get_func_namespace(Oid funcid)
Definition lsyscache.c:1852
@ RTE_CTE
@ RTE_NAMEDTUPLESTORE
@ RTE_VALUES
@ RTE_FUNCTION
@ RTE_TABLEFUNC
@ RTE_RELATION
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
#define linitial(l)
Definition pg_list.h:178
@ TFT_XMLTABLE
Definition primnodes.h:101
@ TFT_JSON_TABLE
Definition primnodes.h:102
TableFuncType functype
Definition primnodes.h:115

References appendStringInfo(), appendStringInfoString(), Assert, elog, ERROR, EXPLAIN_FORMAT_TEXT, ExplainPropertyText(), fb(), ExplainState::format, RangeTblFunction::funcexpr, FuncExpr::funcid, 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, 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 4252 of file explain.c.

4253{
4254 bool has_shared;
4255 bool has_local;
4256 bool has_temp;
4257 bool has_shared_timing;
4258 bool has_local_timing;
4259 bool has_temp_timing;
4260
4261 if (usage == NULL)
4262 return false;
4263
4264 if (es->format != EXPLAIN_FORMAT_TEXT)
4265 return true;
4266
4267 has_shared = (usage->shared_blks_hit > 0 ||
4268 usage->shared_blks_read > 0 ||
4269 usage->shared_blks_dirtied > 0 ||
4270 usage->shared_blks_written > 0);
4271 has_local = (usage->local_blks_hit > 0 ||
4272 usage->local_blks_read > 0 ||
4273 usage->local_blks_dirtied > 0 ||
4274 usage->local_blks_written > 0);
4275 has_temp = (usage->temp_blks_read > 0 ||
4276 usage->temp_blks_written > 0);
4277 has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
4278 !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
4279 has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
4280 !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
4281 has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
4282 !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
4283
4286}
#define INSTR_TIME_IS_ZERO(t)
Definition instr_time.h:419
static void usage(void)

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

Referenced by ExplainOnePlan(), and ExplainPrintSerialize().

◆ plan_is_disabled()

static bool plan_is_disabled ( Plan plan)
static

Definition at line 1258 of file explain.c.

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

References CustomScan::custom_plans, Plan::disabled_nodes, fb(), innerPlan, IsA, lfirst, outerPlan, and plan.

Referenced by ExplainNode().

◆ print_io_usage()

static void print_io_usage ( ExplainState es,
IOStats stats 
)
static

Definition at line 4009 of file explain.c.

4010{
4011 /* don't print prefetch stats if there's nothing to report */
4012 if (stats->prefetch_count > 0)
4013 {
4014 if (es->format == EXPLAIN_FORMAT_TEXT)
4015 {
4016 /* prefetch distance info */
4018 appendStringInfo(es->str, "Prefetch: avg=%.2f max=%d capacity=%d\n",
4019 (stats->distance_sum * 1.0 / stats->prefetch_count),
4020 stats->distance_max,
4021 stats->distance_capacity);
4022
4023 /* prefetch I/O info (only if there were actual I/Os) */
4024 if (stats->io_count > 0)
4025 {
4027 appendStringInfo(es->str, "I/O: count=%" PRIu64 " waits=%" PRIu64
4028 " size=%.2f in-progress=%.2f\n",
4029 stats->io_count, stats->wait_count,
4030 (stats->io_nblocks * 1.0 / stats->io_count),
4031 (stats->io_in_progress * 1.0 / stats->io_count));
4032 }
4033 }
4034 else
4035 {
4036 ExplainPropertyFloat("Average Prefetch Distance", NULL,
4037 (stats->distance_sum * 1.0 / stats->prefetch_count), 3, es);
4038 ExplainPropertyInteger("Max Prefetch Distance", NULL,
4039 stats->distance_max, es);
4040 ExplainPropertyInteger("Prefetch Capacity", NULL,
4041 stats->distance_capacity, es);
4042
4043 ExplainPropertyUInteger("I/O Count", NULL,
4044 stats->io_count, es);
4045 ExplainPropertyUInteger("I/O Waits", NULL,
4046 stats->wait_count, es);
4047 ExplainPropertyFloat("Average I/O Size", NULL,
4048 (stats->io_nblocks * 1.0 / Max(1, stats->io_count)), 3, es);
4049 ExplainPropertyFloat("Average I/Os In Progress", NULL,
4050 (stats->io_in_progress * 1.0 / Max(1, stats->io_count)), 3, es);
4051 }
4052 }
4053}
#define Max(x, y)
Definition c.h:1085
int16 distance_capacity
uint64 io_count
uint64 wait_count
int16 distance_max
uint64 io_in_progress
uint64 distance_sum
uint64 prefetch_count
uint64 io_nblocks

References appendStringInfo(), IOStats::distance_capacity, IOStats::distance_max, IOStats::distance_sum, EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyUInteger(), fb(), ExplainState::format, IOStats::io_count, IOStats::io_in_progress, IOStats::io_nblocks, Max, IOStats::prefetch_count, ExplainState::str, and IOStats::wait_count.

Referenced by show_scan_io_usage().

◆ report_triggers()

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

Definition at line 1100 of file explain.c.

1101{
1102 int nt;
1103
1104 if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
1105 return;
1106 for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
1107 {
1108 Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
1109 TriggerInstrumentation *tginstr = rInfo->ri_TrigInstrument + nt;
1110 char *relname;
1111 char *conname = NULL;
1112
1113 /*
1114 * We ignore triggers that were never invoked; they likely aren't
1115 * relevant to the current query type.
1116 */
1117 if (tginstr->firings == 0)
1118 continue;
1119
1120 ExplainOpenGroup("Trigger", NULL, true, es);
1121
1122 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
1123 if (OidIsValid(trig->tgconstraint))
1124 conname = get_constraint_name(trig->tgconstraint);
1125
1126 /*
1127 * In text format, we avoid printing both the trigger name and the
1128 * constraint name unless VERBOSE is specified. In non-text formats
1129 * we just print everything.
1130 */
1131 if (es->format == EXPLAIN_FORMAT_TEXT)
1132 {
1133 if (es->verbose || conname == NULL)
1134 appendStringInfo(es->str, "Trigger %s", trig->tgname);
1135 else
1136 appendStringInfoString(es->str, "Trigger");
1137 if (conname)
1138 appendStringInfo(es->str, " for constraint %s", conname);
1139 if (show_relname)
1140 appendStringInfo(es->str, " on %s", relname);
1141 if (es->timing)
1142 appendStringInfo(es->str, ": time=%.3f calls=%" PRId64 "\n",
1143 INSTR_TIME_GET_MILLISEC(tginstr->instr.total),
1144 tginstr->firings);
1145 else
1146 appendStringInfo(es->str, ": calls=%" PRId64 "\n",
1147 tginstr->firings);
1148 }
1149 else
1150 {
1151 ExplainPropertyText("Trigger Name", trig->tgname, es);
1152 if (conname)
1153 ExplainPropertyText("Constraint Name", conname, es);
1154 ExplainPropertyText("Relation", relname, es);
1155 if (es->timing)
1156 ExplainPropertyFloat("Time", "ms",
1157 INSTR_TIME_GET_MILLISEC(tginstr->instr.total), 3,
1158 es);
1159 ExplainPropertyInteger("Calls", NULL, tginstr->firings, es);
1160 }
1161
1162 if (conname)
1163 pfree(conname);
1164
1165 ExplainCloseGroup("Trigger", NULL, true, es);
1166 }
1167}
#define OidIsValid(objectId)
Definition c.h:858
char * get_constraint_name(Oid conoid)
Definition lsyscache.c:1227
NameData relname
Definition pg_class.h:40
#define RelationGetRelationName(relation)
Definition rel.h:550

References appendStringInfo(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyText(), fb(), ExplainState::format, get_constraint_name(), INSTR_TIME_GET_MILLISEC, OidIsValid, pfree(), RelationGetRelationName, relname, ExplainState::str, ExplainState::timing, and ExplainState::verbose.

Referenced by ExplainPrintTriggers().

◆ show_agg_keys()

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

Definition at line 2633 of file explain.c.

2635{
2636 Agg *plan = (Agg *) astate->ss.ps.plan;
2637
2638 if (plan->numCols > 0 || plan->groupingSets)
2639 {
2640 /* The key columns refer to the tlist of the child plan */
2641 ancestors = lcons(plan, ancestors);
2642
2643 if (plan->groupingSets)
2644 show_grouping_sets(outerPlanState(astate), plan, ancestors, es);
2645 else
2646 show_sort_group_keys(outerPlanState(astate), "Group Key",
2647 plan->numCols, 0, plan->grpColIdx,
2648 NULL, NULL, NULL,
2649 ancestors, es);
2650
2651 ancestors = list_delete_first(ancestors);
2652 }
2653}
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:2785
static void show_grouping_sets(PlanState *planstate, Agg *agg, List *ancestors, ExplainState *es)
Definition explain.c:2656
ScanState ss
Definition execnodes.h:2432
PlanState ps
Definition execnodes.h:1659

References fb(), 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 4292 of file explain.c.

4293{
4294 if (es->format == EXPLAIN_FORMAT_TEXT)
4295 {
4296 bool has_shared = (usage->shared_blks_hit > 0 ||
4297 usage->shared_blks_read > 0 ||
4298 usage->shared_blks_dirtied > 0 ||
4299 usage->shared_blks_written > 0);
4300 bool has_local = (usage->local_blks_hit > 0 ||
4301 usage->local_blks_read > 0 ||
4302 usage->local_blks_dirtied > 0 ||
4303 usage->local_blks_written > 0);
4304 bool has_temp = (usage->temp_blks_read > 0 ||
4305 usage->temp_blks_written > 0);
4306 bool has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
4307 !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
4308 bool has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
4309 !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
4310 bool has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
4311 !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
4312
4313 /* Show only positive counter values. */
4314 if (has_shared || has_local || has_temp)
4315 {
4317 appendStringInfoString(es->str, "Buffers:");
4318
4319 if (has_shared)
4320 {
4321 appendStringInfoString(es->str, " shared");
4322 if (usage->shared_blks_hit > 0)
4323 appendStringInfo(es->str, " hit=%" PRId64,
4324 usage->shared_blks_hit);
4325 if (usage->shared_blks_read > 0)
4326 appendStringInfo(es->str, " read=%" PRId64,
4327 usage->shared_blks_read);
4328 if (usage->shared_blks_dirtied > 0)
4329 appendStringInfo(es->str, " dirtied=%" PRId64,
4330 usage->shared_blks_dirtied);
4331 if (usage->shared_blks_written > 0)
4332 appendStringInfo(es->str, " written=%" PRId64,
4333 usage->shared_blks_written);
4334 if (has_local || has_temp)
4335 appendStringInfoChar(es->str, ',');
4336 }
4337 if (has_local)
4338 {
4339 appendStringInfoString(es->str, " local");
4340 if (usage->local_blks_hit > 0)
4341 appendStringInfo(es->str, " hit=%" PRId64,
4342 usage->local_blks_hit);
4343 if (usage->local_blks_read > 0)
4344 appendStringInfo(es->str, " read=%" PRId64,
4345 usage->local_blks_read);
4346 if (usage->local_blks_dirtied > 0)
4347 appendStringInfo(es->str, " dirtied=%" PRId64,
4348 usage->local_blks_dirtied);
4349 if (usage->local_blks_written > 0)
4350 appendStringInfo(es->str, " written=%" PRId64,
4351 usage->local_blks_written);
4352 if (has_temp)
4353 appendStringInfoChar(es->str, ',');
4354 }
4355 if (has_temp)
4356 {
4357 appendStringInfoString(es->str, " temp");
4358 if (usage->temp_blks_read > 0)
4359 appendStringInfo(es->str, " read=%" PRId64,
4360 usage->temp_blks_read);
4361 if (usage->temp_blks_written > 0)
4362 appendStringInfo(es->str, " written=%" PRId64,
4363 usage->temp_blks_written);
4364 }
4365 appendStringInfoChar(es->str, '\n');
4366 }
4367
4368 /* As above, show only positive counter values. */
4370 {
4372 appendStringInfoString(es->str, "I/O Timings:");
4373
4375 {
4376 appendStringInfoString(es->str, " shared");
4377 if (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time))
4378 appendStringInfo(es->str, " read=%0.3f",
4379 INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time));
4380 if (!INSTR_TIME_IS_ZERO(usage->shared_blk_write_time))
4381 appendStringInfo(es->str, " write=%0.3f",
4382 INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time));
4384 appendStringInfoChar(es->str, ',');
4385 }
4386 if (has_local_timing)
4387 {
4388 appendStringInfoString(es->str, " local");
4389 if (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time))
4390 appendStringInfo(es->str, " read=%0.3f",
4391 INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time));
4392 if (!INSTR_TIME_IS_ZERO(usage->local_blk_write_time))
4393 appendStringInfo(es->str, " write=%0.3f",
4394 INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time));
4395 if (has_temp_timing)
4396 appendStringInfoChar(es->str, ',');
4397 }
4398 if (has_temp_timing)
4399 {
4400 appendStringInfoString(es->str, " temp");
4401 if (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time))
4402 appendStringInfo(es->str, " read=%0.3f",
4403 INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time));
4404 if (!INSTR_TIME_IS_ZERO(usage->temp_blk_write_time))
4405 appendStringInfo(es->str, " write=%0.3f",
4406 INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time));
4407 }
4408 appendStringInfoChar(es->str, '\n');
4409 }
4410 }
4411 else
4412 {
4413 ExplainPropertyInteger("Shared Hit Blocks", NULL,
4414 usage->shared_blks_hit, es);
4415 ExplainPropertyInteger("Shared Read Blocks", NULL,
4416 usage->shared_blks_read, es);
4417 ExplainPropertyInteger("Shared Dirtied Blocks", NULL,
4418 usage->shared_blks_dirtied, es);
4419 ExplainPropertyInteger("Shared Written Blocks", NULL,
4420 usage->shared_blks_written, es);
4421 ExplainPropertyInteger("Local Hit Blocks", NULL,
4422 usage->local_blks_hit, es);
4423 ExplainPropertyInteger("Local Read Blocks", NULL,
4424 usage->local_blks_read, es);
4425 ExplainPropertyInteger("Local Dirtied Blocks", NULL,
4426 usage->local_blks_dirtied, es);
4427 ExplainPropertyInteger("Local Written Blocks", NULL,
4428 usage->local_blks_written, es);
4429 ExplainPropertyInteger("Temp Read Blocks", NULL,
4430 usage->temp_blks_read, es);
4431 ExplainPropertyInteger("Temp Written Blocks", NULL,
4432 usage->temp_blks_written, es);
4433 if (track_io_timing)
4434 {
4435 ExplainPropertyFloat("Shared I/O Read Time", "ms",
4436 INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time),
4437 3, es);
4438 ExplainPropertyFloat("Shared I/O Write Time", "ms",
4439 INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time),
4440 3, es);
4441 ExplainPropertyFloat("Local I/O Read Time", "ms",
4442 INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time),
4443 3, es);
4444 ExplainPropertyFloat("Local I/O Write Time", "ms",
4445 INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time),
4446 3, es);
4447 ExplainPropertyFloat("Temp I/O Read Time", "ms",
4448 INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time),
4449 3, es);
4450 ExplainPropertyFloat("Temp I/O Write Time", "ms",
4451 INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time),
4452 3, es);
4453 }
4454 }
4455}
bool track_io_timing
Definition bufmgr.c:192

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyFloat(), ExplainPropertyInteger(), fb(), 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 3530 of file explain.c.

3531{
3532 char *maxStorageType;
3534
3535 Tuplestorestate *tupstore = ctescanstate->leader->cte_table;
3536
3537 if (!es->analyze || tupstore == NULL)
3538 return;
3539
3542}
int64_t int64
Definition c.h:621
static void show_storage_info(char *maxStorageType, int64 maxSpaceUsed, ExplainState *es)
Definition explain.c:3012
void tuplestore_get_stats(Tuplestorestate *state, char **max_storage_type, int64 *max_space)

References ExplainState::analyze, fb(), 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 2513 of file explain.c.

2516{
2517 List *context;
2518 char *exprstr;
2519
2520 /* Set up deparsing context */
2522 planstate->plan,
2523 ancestors);
2524
2525 /* Deparse the expression */
2526 exprstr = deparse_expression(node, context, useprefix, false);
2527
2528 /* And add to es->str */
2530}
List * set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
Definition ruleutils.c:4187
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition ruleutils.c:4007

References config_generic::context, ExplainState::deparse_cxt, deparse_expression(), ExplainPropertyText(), fb(), 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 4200 of file explain.c.

4201{
4202 FdwRoutine *fdwroutine = fsstate->fdwroutine;
4203
4204 /* Let the FDW emit whatever fields it wants */
4205 if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
4206 {
4207 if (fdwroutine->ExplainDirectModify != NULL)
4208 fdwroutine->ExplainDirectModify(fsstate, es);
4209 }
4210 else
4211 {
4212 if (fdwroutine->ExplainForeignScan != NULL)
4213 fdwroutine->ExplainForeignScan(fsstate, es);
4214 }
4215}
ExplainForeignScan_function ExplainForeignScan
Definition fdwapi.h:256
ExplainDirectModify_function ExplainDirectModify
Definition fdwapi.h:258
struct FdwRoutine * fdwroutine
Definition execnodes.h:2100

References CMD_SELECT, FdwRoutine::ExplainDirectModify, FdwRoutine::ExplainForeignScan, fb(), 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 2765 of file explain.c.

2767{
2768 Group *plan = (Group *) gstate->ss.ps.plan;
2769
2770 /* The key columns refer to the tlist of the child plan */
2771 ancestors = lcons(plan, ancestors);
2773 plan->numCols, 0, plan->grpColIdx,
2774 NULL, NULL, NULL,
2775 ancestors, es);
2776 ancestors = list_delete_first(ancestors);
2777}
Plan plan
Definition plannodes.h:1182

References fb(), lcons(), list_delete_first(), outerPlanState, Group::plan, plan, and show_sort_group_keys().

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

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

References AGG_HASHED, AGG_MIXED, Agg::aggstrategy, config_generic::context, deparse_expression(), elog, ERROR, EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyListNested(), ExplainPropertyText(), TargetEntry::expr, fb(), ExplainState::format, get_tle_by_resno(), Agg::groupingSets, i, ExplainState::indent, lappend(), lfirst, lfirst_int, NIL, Sort::numCols, PlanState::plan, plan, result, 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 2656 of file explain.c.

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

References config_generic::context, ExplainState::deparse_cxt, ExplainCloseGroup(), ExplainOpenGroup(), fb(), 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 3392 of file explain.c.

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

References appendStringInfo(), BYTES_TO_KILOBYTES, EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyInteger(), ExplainPropertyUInteger(), fb(), ExplainState::format, SharedHashInfo::hinstrument, i, Max, memcpy(), HashInstrumentation::nbatch, HashInstrumentation::nbatch_original, HashInstrumentation::nbuckets, HashInstrumentation::nbuckets_original, SharedHashInfo::num_workers, 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 3752 of file explain.c.

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

References AGG_HASHED, AGG_MIXED, ExplainState::analyze, appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), BYTES_TO_KILOBYTES, ExplainState::costs, EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyInteger(), fb(), ExplainState::format, AggregateInstrumentation::hash_batches_used, AggregateInstrumentation::hash_disk_used, AggregateInstrumentation::hash_mem_peak, INT64_FORMAT, Agg::plan, 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 3192 of file explain.c.

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

References appendStringInfo(), appendStringInfoSpaces(), appendStringInfoString(), bit(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyInteger(), ExplainPropertyList(), fb(), foreach_current_index, ExplainState::format, ExplainState::indent, initStringInfo(), INT64_FORMAT, lappend(), list_length(), NIL, NUM_TUPLESORTMETHODS, SORT_SPACE_TYPE_DISK, SORT_SPACE_TYPE_MEMORY, ExplainState::str, 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 3306 of file explain.c.

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

References ExplainState::analyze, appendStringInfoChar(), EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainOpenWorker(), fb(), ExplainState::format, IncrementalSortInfo::fullsortGroupInfo, IncrementalSortGroupInfo::groupCount, IncrementalSortInfo::prefixsortGroupInfo, show_incremental_sort_group_info(), 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 2600 of file explain.c.

2602{
2604
2606 plan->sort.numCols, plan->nPresortedCols,
2607 plan->sort.sortColIdx,
2608 plan->sort.sortOperators, plan->sort.collations,
2609 plan->sort.nullsFirst,
2610 ancestors, es);
2611}

References fb(), plan, and show_sort_group_keys().

Referenced by ExplainNode().

◆ show_indexsearches_info()

static void show_indexsearches_info ( PlanState planstate,
ExplainState es 
)
static

Definition at line 3874 of file explain.c.

3875{
3876 Plan *plan = planstate->plan;
3878 uint64 nsearches = 0;
3879
3880 if (!es->analyze)
3881 return;
3882
3883 /* Initialize counters with stats from the local process first */
3884 switch (nodeTag(plan))
3885 {
3886 case T_IndexScan:
3887 {
3888 IndexScanState *indexstate = ((IndexScanState *) planstate);
3889
3890 nsearches = indexstate->iss_Instrument->nsearches;
3891 SharedInfo = indexstate->iss_SharedInfo;
3892 break;
3893 }
3894 case T_IndexOnlyScan:
3895 {
3897
3898 nsearches = indexstate->ioss_Instrument->nsearches;
3899 SharedInfo = indexstate->ioss_SharedInfo;
3900 break;
3901 }
3902 case T_BitmapIndexScan:
3903 {
3905
3906 nsearches = indexstate->biss_Instrument->nsearches;
3907 SharedInfo = indexstate->biss_SharedInfo;
3908 break;
3909 }
3910 default:
3911 break;
3912 }
3913
3914 /* Next get the sum of the counters set within each and every process */
3915 if (SharedInfo)
3916 {
3917 for (int i = 0; i < SharedInfo->num_workers; ++i)
3918 {
3919 IndexScanInstrumentation *winstrument = &SharedInfo->winstrument[i];
3920
3921 nsearches += winstrument->nsearches;
3922 }
3923 }
3924
3925 ExplainPropertyUInteger("Index Searches", NULL, nsearches, es);
3926}
IndexScanInstrumentation * biss_Instrument
Definition execnodes.h:1854
IndexScanInstrumentation * ioss_Instrument
Definition execnodes.h:1813
IndexScanInstrumentation * iss_Instrument
Definition execnodes.h:1762

References ExplainState::analyze, BitmapIndexScanState::biss_Instrument, ExplainPropertyUInteger(), fb(), i, IndexOnlyScanState::ioss_Instrument, IndexScanState::iss_Instrument, nodeTag, IndexScanInstrumentation::nsearches, PlanState::plan, and plan.

Referenced by ExplainNode().

◆ show_instrumentation_count()

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

Definition at line 4171 of file explain.c.

4173{
4174 double nfiltered;
4175 double nloops;
4176
4177 if (!es->analyze || !planstate->instrument)
4178 return;
4179
4180 if (which == 2)
4181 nfiltered = planstate->instrument->nfiltered2;
4182 else
4183 nfiltered = planstate->instrument->nfiltered1;
4184 nloops = planstate->instrument->nloops;
4185
4186 /* In text mode, suppress zero counts; they're not interesting enough */
4187 if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
4188 {
4189 if (nloops > 0)
4190 ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es);
4191 else
4192 ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es);
4193 }
4194}

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

Referenced by ExplainNode(), and show_modifytable_info().

◆ show_material_info()

static void show_material_info ( MaterialState mstate,
ExplainState es 
)
static

Definition at line 3484 of file explain.c.

3485{
3486 char *maxStorageType;
3488
3489 Tuplestorestate *tupstore = mstate->tuplestorestate;
3490
3491 /*
3492 * Nothing to show if ANALYZE option wasn't used or if execution didn't
3493 * get as far as creating the tuplestore.
3494 */
3495 if (!es->analyze || tupstore == NULL)
3496 return;
3497
3500}

References ExplainState::analyze, fb(), show_storage_info(), and tuplestore_get_stats().

Referenced by ExplainNode().

◆ show_memoize_info()

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

Definition at line 3599 of file explain.c.

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

References ExplainState::analyze, appendStringInfo(), appendStringInfoString(), BYTES_TO_KILOBYTES, config_generic::context, ExplainState::costs, ExplainState::deparse_cxt, deparse_expression(), EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyText(), ExplainPropertyUInteger(), fb(), ExplainState::format, initStringInfo(), INT64_FORMAT, lfirst, pfree(), plan, ExplainState::rtable_size, set_deparse_context_plan(), 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 4510 of file explain.c.

4511{
4513 mem_counters->freespace);
4515
4516 if (es->format == EXPLAIN_FORMAT_TEXT)
4517 {
4520 "Memory: used=" INT64_FORMAT "kB allocated=" INT64_FORMAT "kB",
4522 appendStringInfoChar(es->str, '\n');
4523 }
4524 else
4525 {
4526 ExplainPropertyInteger("Memory Used", "kB", memUsedkB, es);
4527 ExplainPropertyInteger("Memory Allocated", "kB", memAllocatedkB, es);
4528 }
4529}

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

Referenced by ExplainOnePlan().

◆ show_merge_append_keys()

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

Definition at line 2617 of file explain.c.

2619{
2621
2622 show_sort_group_keys((PlanState *) mstate, "Sort Key",
2623 plan->numCols, 0, plan->sortColIdx,
2624 plan->sortOperators, plan->collations,
2625 plan->nullsFirst,
2626 ancestors, es);
2627}

References fb(), MergeAppend::plan, plan, 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 4732 of file explain.c.

4734{
4735 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
4736 const char *operation;
4737 const char *foperation;
4738 bool labeltargets;
4739 int j;
4740 List *idxNames = NIL;
4741 ListCell *lst;
4742
4743 switch (node->operation)
4744 {
4745 case CMD_INSERT:
4746 operation = "Insert";
4747 foperation = "Foreign Insert";
4748 break;
4749 case CMD_UPDATE:
4750 operation = "Update";
4751 foperation = "Foreign Update";
4752 break;
4753 case CMD_DELETE:
4754 operation = "Delete";
4755 foperation = "Foreign Delete";
4756 break;
4757 case CMD_MERGE:
4758 operation = "Merge";
4759 /* XXX unsupported for now, but avoid compiler noise */
4760 foperation = "Foreign Merge";
4761 break;
4762 default:
4763 operation = "???";
4764 foperation = "Foreign ???";
4765 break;
4766 }
4767
4768 /*
4769 * Should we explicitly label target relations?
4770 *
4771 * If there's only one target relation, do not list it if it's the
4772 * relation named in the query, or if it has been pruned. (Normally
4773 * mtstate->resultRelInfo doesn't include pruned relations, but a single
4774 * pruned target relation may be present, if all other target relations
4775 * have been pruned. See ExecInitModifyTable().)
4776 */
4777 labeltargets = (mtstate->mt_nrels > 1 ||
4778 (mtstate->mt_nrels == 1 &&
4779 mtstate->resultRelInfo[0].ri_RangeTableIndex != node->nominalRelation &&
4781 mtstate->ps.state->es_unpruned_relids)));
4782
4783 if (labeltargets)
4784 ExplainOpenGroup("Target Tables", "Target Tables", false, es);
4785
4786 for (j = 0; j < mtstate->mt_nrels; j++)
4787 {
4788 ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
4789 FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
4790
4791 if (labeltargets)
4792 {
4793 /* Open a group for this target */
4794 ExplainOpenGroup("Target Table", NULL, true, es);
4795
4796 /*
4797 * In text mode, decorate each target with operation type, so that
4798 * ExplainTargetRel's output of " on foo" will read nicely.
4799 */
4800 if (es->format == EXPLAIN_FORMAT_TEXT)
4801 {
4804 fdwroutine ? foperation : operation);
4805 }
4806
4807 /* Identify target */
4808 ExplainTargetRel((Plan *) node,
4809 resultRelInfo->ri_RangeTableIndex,
4810 es);
4811
4812 if (es->format == EXPLAIN_FORMAT_TEXT)
4813 {
4814 appendStringInfoChar(es->str, '\n');
4815 es->indent++;
4816 }
4817 }
4818
4819 /* Give FDW a chance if needed */
4820 if (!resultRelInfo->ri_usesFdwDirectModify &&
4821 fdwroutine != NULL &&
4822 fdwroutine->ExplainForeignModify != NULL)
4823 {
4824 List *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
4825
4826 fdwroutine->ExplainForeignModify(mtstate,
4827 resultRelInfo,
4828 fdw_private,
4829 j,
4830 es);
4831 }
4832
4833 if (labeltargets)
4834 {
4835 /* Undo the indentation we added in text format */
4836 if (es->format == EXPLAIN_FORMAT_TEXT)
4837 es->indent--;
4838
4839 /* Close the group */
4840 ExplainCloseGroup("Target Table", NULL, true, es);
4841 }
4842 }
4843
4844 /* Gather names of ON CONFLICT arbiter indexes */
4845 foreach(lst, node->arbiterIndexes)
4846 {
4847 char *indexname = get_rel_name(lfirst_oid(lst));
4848
4849 idxNames = lappend(idxNames, indexname);
4850 }
4851
4852 if (node->onConflictAction != ONCONFLICT_NONE)
4853 {
4854 const char *resolution = NULL;
4855
4856 if (node->onConflictAction == ONCONFLICT_NOTHING)
4857 resolution = "NOTHING";
4858 else if (node->onConflictAction == ONCONFLICT_UPDATE)
4859 resolution = "UPDATE";
4860 else
4861 {
4862 Assert(node->onConflictAction == ONCONFLICT_SELECT);
4863 switch (node->onConflictLockStrength)
4864 {
4865 case LCS_NONE:
4866 resolution = "SELECT";
4867 break;
4868 case LCS_FORKEYSHARE:
4869 resolution = "SELECT FOR KEY SHARE";
4870 break;
4871 case LCS_FORSHARE:
4872 resolution = "SELECT FOR SHARE";
4873 break;
4874 case LCS_FORNOKEYUPDATE:
4875 resolution = "SELECT FOR NO KEY UPDATE";
4876 break;
4877 case LCS_FORUPDATE:
4878 resolution = "SELECT FOR UPDATE";
4879 break;
4880 }
4881 }
4882
4883 ExplainPropertyText("Conflict Resolution", resolution, es);
4884
4885 /*
4886 * Don't display arbiter indexes at all when DO NOTHING variant
4887 * implicitly ignores all conflicts
4888 */
4889 if (idxNames)
4890 ExplainPropertyList("Conflict Arbiter Indexes", idxNames, es);
4891
4892 /* ON CONFLICT DO SELECT/UPDATE WHERE qual is specially displayed */
4893 if (node->onConflictWhere)
4894 {
4895 show_upper_qual((List *) node->onConflictWhere, "Conflict Filter",
4896 &mtstate->ps, ancestors, es);
4897 show_instrumentation_count("Rows Removed by Conflict Filter", 1, &mtstate->ps, es);
4898 }
4899
4900 /* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
4901 if (es->analyze && mtstate->ps.instrument)
4902 {
4903 double total;
4904 double insert_path;
4905 double other_path;
4906
4907 InstrEndLoop(outerPlanState(mtstate)->instrument);
4908
4909 /* count the number of source rows */
4910 total = outerPlanState(mtstate)->instrument->ntuples;
4911 other_path = mtstate->ps.instrument->ntuples2;
4912 insert_path = total - other_path;
4913
4914 ExplainPropertyFloat("Tuples Inserted", NULL,
4915 insert_path, 0, es);
4916 ExplainPropertyFloat("Conflicting Tuples", NULL,
4917 other_path, 0, es);
4918 }
4919 }
4920 else if (node->operation == CMD_MERGE)
4921 {
4922 /* EXPLAIN ANALYZE display of tuples processed */
4923 if (es->analyze && mtstate->ps.instrument)
4924 {
4925 double total;
4926 double insert_path;
4927 double update_path;
4928 double delete_path;
4929 double skipped_path;
4930
4931 InstrEndLoop(outerPlanState(mtstate)->instrument);
4932
4933 /* count the number of source rows */
4934 total = outerPlanState(mtstate)->instrument->ntuples;
4935 insert_path = mtstate->mt_merge_inserted;
4936 update_path = mtstate->mt_merge_updated;
4937 delete_path = mtstate->mt_merge_deleted;
4939 Assert(skipped_path >= 0);
4940
4941 if (es->format == EXPLAIN_FORMAT_TEXT)
4942 {
4943 if (total > 0)
4944 {
4946 appendStringInfoString(es->str, "Tuples:");
4947 if (insert_path > 0)
4948 appendStringInfo(es->str, " inserted=%.0f", insert_path);
4949 if (update_path > 0)
4950 appendStringInfo(es->str, " updated=%.0f", update_path);
4951 if (delete_path > 0)
4952 appendStringInfo(es->str, " deleted=%.0f", delete_path);
4953 if (skipped_path > 0)
4954 appendStringInfo(es->str, " skipped=%.0f", skipped_path);
4955 appendStringInfoChar(es->str, '\n');
4956 }
4957 }
4958 else
4959 {
4960 ExplainPropertyFloat("Tuples Inserted", NULL, insert_path, 0, es);
4961 ExplainPropertyFloat("Tuples Updated", NULL, update_path, 0, es);
4962 ExplainPropertyFloat("Tuples Deleted", NULL, delete_path, 0, es);
4963 ExplainPropertyFloat("Tuples Skipped", NULL, skipped_path, 0, es);
4964 }
4965 }
4966 }
4967
4968 if (labeltargets)
4969 ExplainCloseGroup("Target Tables", "Target Tables", false, es);
4970}
@ LCS_FORUPDATE
Definition lockoptions.h:28
@ LCS_NONE
Definition lockoptions.h:23
@ LCS_FORSHARE
Definition lockoptions.h:26
@ LCS_FORKEYSHARE
Definition lockoptions.h:25
@ LCS_FORNOKEYUPDATE
Definition lockoptions.h:27
@ ONCONFLICT_NONE
Definition nodes.h:428
@ ONCONFLICT_SELECT
Definition nodes.h:431
@ ONCONFLICT_UPDATE
Definition nodes.h:430
@ ONCONFLICT_NOTHING
Definition nodes.h:429
#define lfirst_oid(lc)
Definition pg_list.h:174
Bitmapset * es_unpruned_relids
Definition execnodes.h:709
ExplainForeignModify_function ExplainForeignModify
Definition fdwapi.h:257
ResultRelInfo * resultRelInfo
Definition execnodes.h:1446
double mt_merge_deleted
Definition execnodes.h:1501
double mt_merge_inserted
Definition execnodes.h:1499
double mt_merge_updated
Definition execnodes.h:1500
Index ri_RangeTableIndex
Definition execnodes.h:510
struct FdwRoutine * ri_FdwRoutine
Definition execnodes.h:566
bool ri_usesFdwDirectModify
Definition execnodes.h:572

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(), fb(), ModifyTable::fdwPrivLists, ExplainState::format, get_rel_name(), ExplainState::indent, InstrEndLoop(), PlanState::instrument, j, lappend(), LCS_FORKEYSHARE, LCS_FORNOKEYUPDATE, LCS_FORSHARE, LCS_FORUPDATE, LCS_NONE, lfirst_oid, list_nth(), ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_inserted, ModifyTableState::mt_merge_updated, ModifyTableState::mt_nrels, NIL, ModifyTable::nominalRelation, NodeInstrumentation::ntuples2, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_SELECT, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictLockStrength, ModifyTable::onConflictWhere, operation, 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 2455 of file explain.c.

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

References CMD_SELECT, config_generic::context, ExplainState::deparse_cxt, deparse_expression(), ExplainPropertyList(), fb(), IsA, lappend(), lfirst, NIL, operation, PlanState::plan, plan, result, 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 2536 of file explain.c.

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

References fb(), 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 3568 of file explain.c.

3569{
3570 char *maxStorageType,
3574
3575 if (!es->analyze)
3576 return;
3577
3578 /*
3579 * Recursive union node uses two tuplestores. We employ the storage type
3580 * from one of them which consumed more memory/disk than the other. The
3581 * storage size is sum of the two.
3582 */
3584 &tempSpaceUsed);
3586 &maxSpaceUsed);
3587
3590
3593}
Tuplestorestate * working_table
Definition execnodes.h:1606
Tuplestorestate * intermediate_table
Definition execnodes.h:1607

References ExplainState::analyze, fb(), 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 4976 of file explain.c.

4977{
4979 int nrels = 0;
4980 int rti = -1;
4981 bool found_non_result = false;
4982 char *replacement_type = "???";
4983
4984 /* If the Result node has a subplan, it didn't replace anything. */
4985 if (result->plan.lefttree != NULL)
4986 return;
4987
4988 /* Gating result nodes should have a subplan, and we don't. */
4989 Assert(result->result_type != RESULT_TYPE_GATING);
4990
4991 switch (result->result_type)
4992 {
4993 case RESULT_TYPE_GATING:
4994 replacement_type = "Gating";
4995 break;
4996 case RESULT_TYPE_SCAN:
4997 replacement_type = "Scan";
4998 break;
4999 case RESULT_TYPE_JOIN:
5000 replacement_type = "Join";
5001 break;
5002 case RESULT_TYPE_UPPER:
5003 /* a small white lie */
5004 replacement_type = "Aggregate";
5005 break;
5006 case RESULT_TYPE_MINMAX:
5007 replacement_type = "MinMaxAggregate";
5008 break;
5009 }
5010
5011 /*
5012 * Build up a comma-separated list of user-facing names for the range
5013 * table entries in the relids set.
5014 */
5016 while ((rti = bms_next_member(result->relids, rti)) >= 0)
5017 {
5018 RangeTblEntry *rte = rt_fetch(rti, es->rtable);
5019 char *refname;
5020
5021 /*
5022 * add_outer_joins_to_relids will add join RTIs to the relids set of a
5023 * join; if that join is then replaced with a Result node, we may see
5024 * such RTIs here. But we want to completely ignore those here,
5025 * because "a LEFT JOIN b ON whatever" is a join between a and b, not
5026 * a join between a, b, and an unnamed join.
5027 */
5028 if (rte->rtekind == RTE_JOIN)
5029 continue;
5030
5031 /* Count the number of rels that aren't ignored completely. */
5032 ++nrels;
5033
5034 /* Work out what reference name to use and add it to the string. */
5035 refname = (char *) list_nth(es->rtable_names, rti - 1);
5036 if (refname == NULL)
5037 refname = rte->eref->aliasname;
5038 if (buf.len > 0)
5040 appendStringInfoString(&buf, refname);
5041
5042 /* Keep track of whether we see anything other than RTE_RESULT. */
5043 if (rte->rtekind != RTE_RESULT)
5044 found_non_result = true;
5045 }
5046
5047 /*
5048 * If this Result node is because of a single RTE that is RTE_RESULT, it
5049 * is not really replacing anything at all, because there's no other
5050 * method for implementing a scan of such an RTE, so we don't display the
5051 * Replaces line in such cases.
5052 */
5053 if (nrels <= 1 && !found_non_result &&
5054 result->result_type == RESULT_TYPE_SCAN)
5055 return;
5056
5057 /* Say what we replaced, with list of rels if available. */
5058 if (buf.len == 0)
5059 ExplainPropertyText("Replaces", replacement_type, es);
5060 else
5061 {
5062 char *s = psprintf("%s on %s", replacement_type, buf.data);
5063
5064 ExplainPropertyText("Replaces", s, es);
5065 }
5066}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
@ RTE_JOIN
@ RTE_RESULT
static char buf[DEFAULT_XLOG_SEG_SIZE]
@ RESULT_TYPE_UPPER
Definition plannodes.h:280
@ RESULT_TYPE_SCAN
Definition plannodes.h:278
@ RESULT_TYPE_GATING
Definition plannodes.h:277
@ RESULT_TYPE_MINMAX
Definition plannodes.h:281
@ RESULT_TYPE_JOIN
Definition plannodes.h:279

References appendStringInfoString(), Assert, bms_next_member(), buf, ExplainPropertyText(), fb(), initStringInfo(), list_nth(), psprintf(), result, RESULT_TYPE_GATING, RESULT_TYPE_JOIN, RESULT_TYPE_MINMAX, RESULT_TYPE_SCAN, RESULT_TYPE_UPPER, rt_fetch, ExplainState::rtable, ExplainState::rtable_names, RTE_JOIN, and RTE_RESULT.

Referenced by ExplainNode().

◆ show_scan_io_usage()

static void show_scan_io_usage ( ScanState planstate,
ExplainState es 
)
static

Definition at line 4059 of file explain.c.

4060{
4061 Plan *plan = planstate->ps.plan;
4062 IOStats stats = {0};
4063
4064 if (!es->io)
4065 return;
4066
4067 /*
4068 * Initialize counters with stats from the local process first.
4069 *
4070 * The scan descriptor may not exist, e.g. if the scan did not start, or
4071 * because of debug_parallel_query=regress. We still want to collect data
4072 * from workers.
4073 */
4074 if (planstate->ss_currentScanDesc &&
4076 {
4077 stats = planstate->ss_currentScanDesc->rs_instrument->io;
4078 }
4079
4080 /*
4081 * Accumulate data from parallel workers (if any).
4082 */
4083 switch (nodeTag(plan))
4084 {
4085 case T_BitmapHeapScan:
4086 {
4088 = ((BitmapHeapScanState *) planstate)->sinstrument;
4089
4090 if (sinstrument)
4091 {
4092 for (int i = 0; i < sinstrument->num_workers; ++i)
4093 {
4094 BitmapHeapScanInstrumentation *winstrument = &sinstrument->sinstrument[i];
4095
4096 AccumulateIOStats(&stats, &winstrument->stats.io);
4097
4098 if (!es->workers_state)
4099 continue;
4100
4101 ExplainOpenWorker(i, es);
4102 print_io_usage(es, &winstrument->stats.io);
4103 ExplainCloseWorker(i, es);
4104 }
4105 }
4106
4107 break;
4108 }
4109 case T_SeqScan:
4110 {
4111 SharedSeqScanInstrumentation *sinstrument
4112 = ((SeqScanState *) planstate)->sinstrument;
4113
4114 if (sinstrument)
4115 {
4116 for (int i = 0; i < sinstrument->num_workers; ++i)
4117 {
4118 SeqScanInstrumentation *winstrument = &sinstrument->sinstrument[i];
4119
4120 AccumulateIOStats(&stats, &winstrument->stats.io);
4121
4122 if (!es->workers_state)
4123 continue;
4124
4125 ExplainOpenWorker(i, es);
4126 print_io_usage(es, &winstrument->stats.io);
4127 ExplainCloseWorker(i, es);
4128 }
4129 }
4130
4131 break;
4132 }
4133 case T_TidRangeScan:
4134 {
4136 = ((TidRangeScanState *) planstate)->trss_sinstrument;
4137
4138 if (sinstrument)
4139 {
4140 for (int i = 0; i < sinstrument->num_workers; ++i)
4141 {
4142 TidRangeScanInstrumentation *winstrument = &sinstrument->sinstrument[i];
4143
4144 AccumulateIOStats(&stats, &winstrument->stats.io);
4145
4146 if (!es->workers_state)
4147 continue;
4148
4149 ExplainOpenWorker(i, es);
4150 print_io_usage(es, &winstrument->stats.io);
4151 ExplainCloseWorker(i, es);
4152 }
4153 }
4154
4155 break;
4156 }
4157 default:
4158 /* ignore other plans */
4159 return;
4160 }
4161
4162 print_io_usage(es, &stats);
4163}
static void print_io_usage(ExplainState *es, IOStats *stats)
Definition explain.c:4009
static void AccumulateIOStats(IOStats *dst, IOStats *src)
TableScanInstrumentation stats
struct TableScanDescData * ss_currentScanDesc
Definition execnodes.h:1661
TableScanInstrumentation stats
BitmapHeapScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
SeqScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
TidRangeScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
struct TableScanInstrumentation * rs_instrument
Definition relscan.h:72
TableScanInstrumentation stats

References AccumulateIOStats(), ExplainCloseWorker(), ExplainOpenWorker(), fb(), i, ExplainState::io, TableScanInstrumentation::io, nodeTag, SharedBitmapHeapInstrumentation::num_workers, SharedSeqScanInstrumentation::num_workers, SharedTidRangeScanInstrumentation::num_workers, PlanState::plan, plan, print_io_usage(), ScanState::ps, TableScanDescData::rs_instrument, SharedBitmapHeapInstrumentation::sinstrument, SharedSeqScanInstrumentation::sinstrument, SharedTidRangeScanInstrumentation::sinstrument, ScanState::ss_currentScanDesc, BitmapHeapScanInstrumentation::stats, SeqScanInstrumentation::stats, TidRangeScanInstrumentation::stats, and ExplainState::workers_state.

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

2560{
2561 bool useprefix;
2562
2563 useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
2564 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2565}
static void show_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
Definition explain.c:2536

References fb(), 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 2785 of file explain.c.

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

References appendStringInfoString(), config_generic::context, ExplainState::deparse_cxt, deparse_expression(), elog, ERROR, ExplainPropertyList(), TargetEntry::expr, fb(), get_tle_by_resno(), initStringInfo(), lappend(), NIL, PlanState::plan, plan, pstrdup(), resetStringInfo(), result, 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 3101 of file explain.c.

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

References ExplainState::analyze, appendStringInfo(), EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyInteger(), ExplainPropertyText(), fb(), 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 2585 of file explain.c.

2586{
2587 Sort *plan = (Sort *) sortstate->ss.ps.plan;
2588
2589 show_sort_group_keys((PlanState *) sortstate, "Sort Key",
2590 plan->numCols, 0, plan->sortColIdx,
2591 plan->sortOperators, plan->collations,
2592 plan->nullsFirst,
2593 ancestors, es);
2594}
ScanState ss
Definition execnodes.h:2356

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

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

References appendStringInfo(), appendStringInfoString(), buf, elog, ERROR, exprType(), fb(), 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

◆ show_table_func_scan_info()

static void show_table_func_scan_info ( TableFuncScanState tscanstate,
ExplainState es 
)
static

Definition at line 3549 of file explain.c.

3550{
3551 char *maxStorageType;
3553
3554 Tuplestorestate *tupstore = tscanstate->tupstore;
3555
3556 if (!es->analyze || tupstore == NULL)
3557 return;
3558
3561}

References ExplainState::analyze, fb(), show_storage_info(), and tuplestore_get_stats().

Referenced by ExplainNode().

◆ show_tablesample()

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

Definition at line 3035 of file explain.c.

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

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

Referenced by ExplainNode().

◆ show_tidbitmap_info()

static void show_tidbitmap_info ( BitmapHeapScanState planstate,
ExplainState es 
)
static

Definition at line 3932 of file explain.c.

3933{
3934 if (!es->analyze)
3935 return;
3936
3937 if (es->format != EXPLAIN_FORMAT_TEXT)
3938 {
3939 ExplainPropertyUInteger("Exact Heap Blocks", NULL,
3940 planstate->stats.exact_pages, es);
3941 ExplainPropertyUInteger("Lossy Heap Blocks", NULL,
3942 planstate->stats.lossy_pages, es);
3943 }
3944 else
3945 {
3946 if (planstate->stats.exact_pages > 0 || planstate->stats.lossy_pages > 0)
3947 {
3949 appendStringInfoString(es->str, "Heap Blocks:");
3950 if (planstate->stats.exact_pages > 0)
3951 appendStringInfo(es->str, " exact=" UINT64_FORMAT, planstate->stats.exact_pages);
3952 if (planstate->stats.lossy_pages > 0)
3953 appendStringInfo(es->str, " lossy=" UINT64_FORMAT, planstate->stats.lossy_pages);
3954 appendStringInfoChar(es->str, '\n');
3955 }
3956 }
3957
3958 /* Display stats for each parallel worker */
3959 if (planstate->sinstrument != NULL)
3960 {
3961 for (int n = 0; n < planstate->sinstrument->num_workers; n++)
3962 {
3964
3965 if (si->exact_pages == 0 && si->lossy_pages == 0)
3966 continue;
3967
3968 if (es->workers_state)
3969 ExplainOpenWorker(n, es);
3970
3971 if (es->format == EXPLAIN_FORMAT_TEXT)
3972 {
3974 appendStringInfoString(es->str, "Heap Blocks:");
3975 if (si->exact_pages > 0)
3976 appendStringInfo(es->str, " exact=" UINT64_FORMAT, si->exact_pages);
3977 if (si->lossy_pages > 0)
3978 appendStringInfo(es->str, " lossy=" UINT64_FORMAT, si->lossy_pages);
3979 appendStringInfoChar(es->str, '\n');
3980 }
3981 else
3982 {
3983 ExplainPropertyUInteger("Exact Heap Blocks", NULL,
3984 si->exact_pages, es);
3985 ExplainPropertyUInteger("Lossy Heap Blocks", NULL,
3986 si->lossy_pages, es);
3987 }
3988
3989 if (es->workers_state)
3990 ExplainCloseWorker(n, es);
3991 }
3992 }
3993}
BitmapHeapScanInstrumentation stats
Definition execnodes.h:1880
SharedBitmapHeapInstrumentation * sinstrument
Definition execnodes.h:1883

References ExplainState::analyze, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), BitmapHeapScanInstrumentation::exact_pages, EXPLAIN_FORMAT_TEXT, ExplainCloseWorker(), ExplainIndentText(), ExplainOpenWorker(), ExplainPropertyUInteger(), fb(), ExplainState::format, BitmapHeapScanInstrumentation::lossy_pages, SharedBitmapHeapInstrumentation::num_workers, 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 2571 of file explain.c.

2574{
2575 bool useprefix;
2576
2577 useprefix = (es->rtable_size > 1 || es->verbose);
2578 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2579}

References fb(), 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 4461 of file explain.c.

4462{
4463 if (es->format == EXPLAIN_FORMAT_TEXT)
4464 {
4465 /* Show only positive counter values. */
4466 if ((usage->wal_records > 0) || (usage->wal_fpi > 0) ||
4467 (usage->wal_bytes > 0) || (usage->wal_buffers_full > 0) ||
4468 (usage->wal_fpi_bytes > 0))
4469 {
4471 appendStringInfoString(es->str, "WAL:");
4472
4473 if (usage->wal_records > 0)
4474 appendStringInfo(es->str, " records=%" PRId64,
4475 usage->wal_records);
4476 if (usage->wal_fpi > 0)
4477 appendStringInfo(es->str, " fpi=%" PRId64,
4478 usage->wal_fpi);
4479 if (usage->wal_bytes > 0)
4480 appendStringInfo(es->str, " bytes=%" PRIu64,
4481 usage->wal_bytes);
4482 if (usage->wal_fpi_bytes > 0)
4483 appendStringInfo(es->str, " fpi bytes=%" PRIu64,
4484 usage->wal_fpi_bytes);
4485 if (usage->wal_buffers_full > 0)
4486 appendStringInfo(es->str, " buffers full=%" PRId64,
4487 usage->wal_buffers_full);
4488 appendStringInfoChar(es->str, '\n');
4489 }
4490 }
4491 else
4492 {
4493 ExplainPropertyInteger("WAL Records", NULL,
4494 usage->wal_records, es);
4495 ExplainPropertyInteger("WAL FPI", NULL,
4496 usage->wal_fpi, es);
4497 ExplainPropertyUInteger("WAL Bytes", NULL,
4498 usage->wal_bytes, es);
4499 ExplainPropertyUInteger("WAL FPI Bytes", NULL,
4500 usage->wal_fpi_bytes, es);
4501 ExplainPropertyInteger("WAL Buffers Full", NULL,
4502 usage->wal_buffers_full, es);
4503 }
4504}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyInteger(), ExplainPropertyUInteger(), fb(), 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 2905 of file explain.c.

2906{
2907 WindowAgg *wagg = (WindowAgg *) planstate->ss.ps.plan;
2909 bool needspace = false;
2910
2912 appendStringInfo(&wbuf, "%s AS (", quote_identifier(wagg->winname));
2913
2914 /* The key columns refer to the tlist of the child plan */
2915 ancestors = lcons(wagg, ancestors);
2916 if (wagg->partNumCols > 0)
2917 {
2918 appendStringInfoString(&wbuf, "PARTITION BY ");
2920 wagg->partNumCols, wagg->partColIdx,
2921 ancestors, es);
2922 needspace = true;
2923 }
2924 if (wagg->ordNumCols > 0)
2925 {
2926 if (needspace)
2928 appendStringInfoString(&wbuf, "ORDER BY ");
2930 wagg->ordNumCols, wagg->ordColIdx,
2931 ancestors, es);
2932 needspace = true;
2933 }
2934 ancestors = list_delete_first(ancestors);
2935 if (wagg->frameOptions & FRAMEOPTION_NONDEFAULT)
2936 {
2937 List *context;
2938 bool useprefix;
2939 char *framestr;
2940
2941 /* Set up deparsing context for possible frame expressions */
2943 (Plan *) wagg,
2944 ancestors);
2945 useprefix = (es->rtable_size > 1 || es->verbose);
2947 wagg->startOffset,
2948 wagg->endOffset,
2949 context,
2950 useprefix);
2951 if (needspace)
2954 pfree(framestr);
2955 }
2957 ExplainPropertyText("Window", wbuf.data, es);
2958 pfree(wbuf.data);
2959}
static void show_window_keys(StringInfo buf, PlanState *planstate, int nkeys, AttrNumber *keycols, List *ancestors, ExplainState *es)
Definition explain.c:2968
#define FRAMEOPTION_NONDEFAULT
Definition parsenodes.h:612
char * get_window_frame_options_for_explain(int frameOptions, Node *startOffset, Node *endOffset, List *dpcontext, bool forceprefix)
Definition ruleutils.c:7275
ScanState ss
Definition execnodes.h:2529

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

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

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

References appendStringInfoString(), buf, config_generic::context, ExplainState::deparse_cxt, deparse_expression(), elog, ERROR, TargetEntry::expr, fb(), 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 3507 of file explain.c.

3508{
3509 char *maxStorageType;
3511
3512 Tuplestorestate *tupstore = winstate->buffer;
3513
3514 /*
3515 * Nothing to show if ANALYZE option wasn't used or if execution didn't
3516 * get as far as creating the tuplestore.
3517 */
3518 if (!es->analyze || tupstore == NULL)
3519 return;
3520
3523}
Tuplestorestate * buffer
Definition execnodes.h:2540

References ExplainState::analyze, WindowAggState::buffer, fb(), 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 324 of file explain.c.

328{
332 BufferUsage bufusage_start,
333 bufusage;
337
338 if (es->memory)
339 {
340 /*
341 * Create a new memory context to measure planner's memory consumption
342 * accurately. Note that if the planner were to be modified to use a
343 * different memory context type, here we would be changing that to
344 * AllocSet, which might be undesirable. However, we don't have a way
345 * to create a context of the same type as another, so we pray and
346 * hope that this is OK.
347 */
349 "explain analyze planner context",
352 }
353
354 if (es->buffers)
355 bufusage_start = pgBufferUsage;
357
358 /* plan the query */
359 plan = pg_plan_query(query, queryString, cursorOptions, params, es);
360
363
364 if (es->memory)
365 {
368 }
369
370 /* calc differences of buffer counters. */
371 if (es->buffers)
372 {
373 memset(&bufusage, 0, sizeof(BufferUsage));
374 BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
375 }
376
377 /* run it (if needed) and produce output */
378 ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
379 &planduration, (es->buffers ? &bufusage : NULL),
380 es->memory ? &mem_counters : NULL);
381}
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:500
BufferUsage pgBufferUsage
Definition instrument.c:25
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition instrument.c:327
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:898

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, ExplainState::buffers, BufferUsageAccumDiff(), CurrentMemoryContext, ExplainOnePlan(), fb(), 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 55 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 59 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 58 of file explain.c.

Referenced by _PG_init(), explain_ExecutorEnd(), and ExplainOnePlan().

◆ ExplainOneQuery_hook

ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL

Definition at line 52 of file explain.c.

Referenced by ExplainOneQuery().