PostgreSQL Source Code git master
Loading...
Searching...
No Matches
explain.c File Reference
#include "postgres.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/createas.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/explain_dr.h"
#include "commands/explain_format.h"
#include "commands/explain_state.h"
#include "commands/prepare.h"
#include "foreign/fdwapi.h"
#include "jit/jit.h"
#include "libpq/pqformat.h"
#include "libpq/protocol.h"
#include "nodes/extensible.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/guc_tables.h"
#include "utils/json.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/tuplesort.h"
#include "utils/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_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)
 

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

Function Documentation

◆ elapsed_time()

static double elapsed_time ( instr_time starttime)
static

Definition at line 1168 of file explain.c.

1169{
1171
1173 INSTR_TIME_SUBTRACT(endtime, *starttime);
1175}
#define INSTR_TIME_SET_CURRENT(t)
Definition instr_time.h:122
#define INSTR_TIME_GET_DOUBLE(t)
Definition instr_time.h:186
#define INSTR_TIME_SUBTRACT(x, y)
Definition instr_time.h:177
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 4051 of file explain.c.

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

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

Referenced by ExplainIndexScanDetails(), and ExplainNode().

◆ ExplainCloseWorker()

static void ExplainCloseWorker ( int  n,
ExplainState es 
)
static

Definition at line 5088 of file explain.c.

5089{
5091
5092 Assert(wstate);
5093 Assert(n >= 0 && n < wstate->num_workers);
5094 Assert(wstate->worker_inited[n]);
5095
5096 /*
5097 * Save formatting state in case we do another ExplainOpenWorker(), then
5098 * pop the formatting stack.
5099 */
5100 ExplainSaveGroup(es, 2, &wstate->worker_state_save[n]);
5101
5102 /*
5103 * In TEXT format, if we didn't actually produce any output line(s) then
5104 * truncate off the partial line emitted by ExplainOpenWorker. (This is
5105 * to avoid bogus output if, say, show_buffer_usage chooses not to print
5106 * anything for the worker.) Also fix up the indent level.
5107 */
5108 if (es->format == EXPLAIN_FORMAT_TEXT)
5109 {
5110 while (es->str->len > 0 && es->str->data[es->str->len - 1] != '\n')
5111 es->str->data[--(es->str->len)] = '\0';
5112
5113 es->indent--;
5114 }
5115
5116 /* Restore prior output buffer pointer */
5117 es->str = wstate->prev_str;
5118}
#define Assert(condition)
Definition c.h:945
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_sort_info(), and show_tidbitmap_info().

◆ ExplainCreateWorkersState()

static ExplainWorkersState * ExplainCreateWorkersState ( int  num_workers)
static

Definition at line 5009 of file explain.c.

5010{
5012
5014 wstate->num_workers = num_workers;
5015 wstate->worker_inited = (bool *) palloc0(num_workers * sizeof(bool));
5016 wstate->worker_str = (StringInfoData *)
5017 palloc0(num_workers * sizeof(StringInfoData));
5018 wstate->worker_state_save = (int *) palloc(num_workers * sizeof(int));
5019 return wstate;
5020}
#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 4987 of file explain.c.

4988{
4989 ListCell *cell;
4990 const char *label =
4991 (list_length(css->custom_ps) != 1 ? "children" : "child");
4992
4993 foreach(cell, css->custom_ps)
4994 ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
4995}
static void ExplainNode(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
Definition explain.c:1360
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 5124 of file explain.c.

5125{
5127
5128 ExplainOpenGroup("Workers", "Workers", false, es);
5129 for (int i = 0; i < wstate->num_workers; i++)
5130 {
5131 if (wstate->worker_inited[i])
5132 {
5133 /* This must match previous ExplainOpenSetAsideGroup call */
5134 ExplainOpenGroup("Worker", NULL, true, es);
5135 appendStringInfoString(es->str, wstate->worker_str[i].data);
5136 ExplainCloseGroup("Worker", NULL, true, es);
5137
5138 pfree(wstate->worker_str[i].data);
5139 }
5140 }
5141 ExplainCloseGroup("Workers", "Workers", false, es);
5142
5143 pfree(wstate->worker_inited);
5144 pfree(wstate->worker_str);
5145 pfree(wstate->worker_state_save);
5146 pfree(wstate);
5147}
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 4359 of file explain.c.

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

4901{
4902 int j;
4903
4904 for (j = 0; j < nplans; j++)
4905 ExplainNode(planstates[j], ancestors,
4906 "Member", NULL, es);
4907}
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 4917 of file explain.c.

4918{
4920 ExplainPropertyInteger("Subplans Removed", NULL,
4921 nchildren - nplans, es);
4922}
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 4408 of file explain.c.

4409{
4410 ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
4411}
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
Definition explain.c:4417
#define plan(x)
Definition pg_regress.c:161

References ExplainTargetRel(), and plan.

Referenced by ExplainNode().

◆ ExplainNode()

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

Definition at line 1360 of file explain.c.

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

References AGG_HASHED, AGG_MIXED, AGG_PLAIN, AGG_SORTED, 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, INSTR_TIME_GET_MILLISEC, InstrEndLoop(), WorkerInstrumentation::instrument, PlanState::instrument, IsA, SharedJitInstrumentation::jit_instr, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_RIGHT_ANTI, JOIN_RIGHT_SEMI, JOIN_SEMI, lappend(), lcons(), lfirst, list_delete_first(), list_length(), list_make1, make_andclause(), make_orclause(), NIL, Instrumentation::nloops, nodeTag, Instrumentation::ntuples, Instrumentation::ntuples2, WorkerInstrumentation::num_workers, SharedJitInstrumentation::num_workers, 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_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(), Instrumentation::startup, PlanState::state, ExplainState::str, PlanState::subPlan, ExplainState::timing, Instrumentation::total, ExplainState::verbose, ExplainState::wal, Instrumentation::walusage, PlanState::worker_instrument, PlanState::worker_jit_instrument, and ExplainState::workers_state.

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

◆ ExplainOnePlan()

void ExplainOnePlan ( PlannedStmt plannedstmt,
IntoClause into,
ExplainState es,
const char queryString,
ParamListInfo  params,
QueryEnvironment queryEnv,
const instr_time planduration,
const BufferUsage bufusage,
const MemoryContextCounters mem_counters 
)

Definition at line 497 of file explain.c.

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

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_ROWS, INSTRUMENT_TIMER, INSTRUMENT_WAL, InvalidSnapshot, NoMovementScanDirection, None_Receiver, peek_buffer_usage(), PopActiveSnapshot(), PushCopiedSnapshot(), ExplainState::serialize, show_buffer_usage(), show_memory_counters(), IntoClause::skipData, ExplainState::str, ExplainState::summary, ExplainState::timing, UpdateActiveSnapshotCommandId(), and ExplainState::wal.

Referenced by ExplainExecuteQuery(), and standard_ExplainOneQuery().

◆ ExplainOneQuery()

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

Definition at line 296 of file explain.c.

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

395{
396 if (utilityStmt == NULL)
397 return;
398
399 if (IsA(utilityStmt, CreateTableAsStmt))
400 {
401 /*
402 * We have to rewrite the contained SELECT and then pass it back to
403 * ExplainOneQuery. Copy to be safe in the EXPLAIN EXECUTE case.
404 */
405 CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
409
410 /*
411 * Check if the relation exists or not. This is done at this stage to
412 * avoid query planning or execution.
413 */
415 {
416 if (ctas->objtype == OBJECT_TABLE)
417 ExplainDummyGroup("CREATE TABLE AS", NULL, es);
418 else if (ctas->objtype == OBJECT_MATVIEW)
419 ExplainDummyGroup("CREATE MATERIALIZED VIEW", NULL, es);
420 else
421 elog(ERROR, "unexpected object type: %d",
422 (int) ctas->objtype);
423 return;
424 }
425
427 if (IsQueryIdEnabled())
430 (*post_parse_analyze_hook) (pstate, ctas_query, jstate);
434 CURSOR_OPT_PARALLEL_OK, ctas->into, es,
435 pstate, params);
436 }
437 else if (IsA(utilityStmt, DeclareCursorStmt))
438 {
439 /*
440 * Likewise for DECLARE CURSOR.
441 *
442 * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
443 * actually run the query. This is different from pre-8.3 behavior
444 * but seems more useful than not running the query. No cursor will
445 * be created, however.
446 */
447 DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
451
453 if (IsQueryIdEnabled())
456 (*post_parse_analyze_hook) (pstate, dcs_query, jstate);
457
461 dcs->options, NULL, es,
462 pstate, params);
463 }
464 else if (IsA(utilityStmt, ExecuteStmt))
465 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
466 pstate, params);
467 else if (IsA(utilityStmt, NotifyStmt))
468 {
469 if (es->format == EXPLAIN_FORMAT_TEXT)
470 appendStringInfoString(es->str, "NOTIFY\n");
471 else
472 ExplainDummyGroup("Notify", NULL, es);
473 }
474 else
475 {
476 if (es->format == EXPLAIN_FORMAT_TEXT)
478 "Utility statements have no plan structure\n");
479 else
480 ExplainDummyGroup("Utility Statement", NULL, es);
481 }
482}
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:296
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:68
#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 5026 of file explain.c.

5027{
5029
5030 Assert(wstate);
5031 Assert(n >= 0 && n < wstate->num_workers);
5032
5033 /* Save prior output buffer pointer */
5034 wstate->prev_str = es->str;
5035
5036 if (!wstate->worker_inited[n])
5037 {
5038 /* First time through, so create the buffer for this worker */
5039 initStringInfo(&wstate->worker_str[n]);
5040 es->str = &wstate->worker_str[n];
5041
5042 /*
5043 * Push suitable initial formatting state for this worker's field
5044 * group. We allow one extra logical nesting level, since this group
5045 * will eventually be wrapped in an outer "Workers" group.
5046 */
5047 ExplainOpenSetAsideGroup("Worker", NULL, true, 2, es);
5048
5049 /*
5050 * In non-TEXT formats we always emit a "Worker Number" field, even if
5051 * there's no other data for this worker.
5052 */
5053 if (es->format != EXPLAIN_FORMAT_TEXT)
5054 ExplainPropertyInteger("Worker Number", NULL, n, es);
5055
5056 wstate->worker_inited[n] = true;
5057 }
5058 else
5059 {
5060 /* Resuming output for a worker we've already emitted some data for */
5061 es->str = &wstate->worker_str[n];
5062
5063 /* Restore formatting state saved by last ExplainCloseWorker() */
5064 ExplainRestoreGroup(es, 2, &wstate->worker_state_save[n]);
5065 }
5066
5067 /*
5068 * In TEXT format, prefix the first output line for this worker with
5069 * "Worker N:". Then, any additional lines should be indented one more
5070 * stop than the "Worker N" line is.
5071 */
5072 if (es->format == EXPLAIN_FORMAT_TEXT)
5073 {
5074 if (es->str->len == 0)
5075 {
5077 appendStringInfo(es->str, "Worker %d: ", n);
5078 }
5079
5080 es->indent++;
5081 }
5082}
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_sort_info(), and show_tidbitmap_info().

◆ ExplainPreScanNode()

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

Definition at line 1187 of file explain.c.

1188{
1189 Plan *plan = planstate->plan;
1190
1191 switch (nodeTag(plan))
1192 {
1193 case T_SeqScan:
1194 case T_SampleScan:
1195 case T_IndexScan:
1196 case T_IndexOnlyScan:
1197 case T_BitmapHeapScan:
1198 case T_TidScan:
1199 case T_TidRangeScan:
1200 case T_SubqueryScan:
1201 case T_FunctionScan:
1202 case T_TableFuncScan:
1203 case T_ValuesScan:
1204 case T_CteScan:
1206 case T_WorkTableScan:
1208 ((Scan *) plan)->scanrelid);
1209 break;
1210 case T_ForeignScan:
1212 ((ForeignScan *) plan)->fs_base_relids);
1213 break;
1214 case T_CustomScan:
1216 ((CustomScan *) plan)->custom_relids);
1217 break;
1218 case T_ModifyTable:
1220 ((ModifyTable *) plan)->nominalRelation);
1221 if (((ModifyTable *) plan)->exclRelRTI)
1223 ((ModifyTable *) plan)->exclRelRTI);
1224 /* Ensure Vars used in RETURNING will have refnames */
1225 if (plan->targetlist)
1227 linitial_int(((ModifyTable *) plan)->resultRelations));
1228 break;
1229 case T_Append:
1231 ((Append *) plan)->apprelids);
1232 break;
1233 case T_MergeAppend:
1235 ((MergeAppend *) plan)->apprelids);
1236 break;
1237 case T_Result:
1239 ((Result *) plan)->relids);
1240 break;
1241 default:
1242 break;
1243 }
1244
1246}
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:1187
#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 904 of file explain.c.

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

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

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

Referenced by explain_ExecutorEnd(), and ExplainOnePlan().

◆ ExplainPrintPlan()

void ExplainPrintPlan ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 762 of file explain.c.

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

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

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

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

836{
838 bool show_relname;
841 List *targrels;
842 ListCell *l;
843
847
848 ExplainOpenGroup("Triggers", "Triggers", false, es);
849
851 routerels != NIL || targrels != NIL);
852 foreach(l, resultrels)
853 {
854 rInfo = (ResultRelInfo *) lfirst(l);
856 }
857
858 foreach(l, routerels)
859 {
860 rInfo = (ResultRelInfo *) lfirst(l);
862 }
863
864 foreach(l, targrels)
865 {
866 rInfo = (ResultRelInfo *) lfirst(l);
868 }
869
870 ExplainCloseGroup("Triggers", "Triggers", false, es);
871}
static void report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
Definition explain.c:1095
List * es_tuple_routing_result_relations
Definition execnodes.h:710
List * es_trig_target_relations
Definition execnodes.h:713
List * es_opened_result_relations
Definition execnodes.h:700

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

180{
184 Query *query;
186
187 /* Configure the ExplainState based on the provided options */
188 ParseExplainOptionList(es, stmt->options, pstate);
189
190 /* Extract the query and, if enabled, jumble it */
191 query = castNode(Query, stmt->query);
192 if (IsQueryIdEnabled())
193 jstate = JumbleQuery(query);
194
196 (*post_parse_analyze_hook) (pstate, query, jstate);
197
198 /*
199 * Parse analysis was done already, but we still have to run the rule
200 * rewriter. We do not do AcquireRewriteLocks: we assume the query either
201 * came straight from the parser, or suitable locks were acquired by
202 * plancache.c.
203 */
205
206 /* emit opening boilerplate */
208
209 if (rewritten == NIL)
210 {
211 /*
212 * In the case of an INSTEAD NOTHING, tell at least that. But in
213 * non-text format, the output is delimited, so this isn't necessary.
214 */
215 if (es->format == EXPLAIN_FORMAT_TEXT)
216 appendStringInfoString(es->str, "Query rewrites to nothing\n");
217 }
218 else
219 {
220 ListCell *l;
221
222 /* Explain every plan */
223 foreach(l, rewritten)
224 {
227 pstate, params);
228
229 /* Separate plans with an appropriate separator */
230 if (lnext(rewritten, l) != NULL)
232 }
233 }
234
235 /* emit closing boilerplate */
237 Assert(es->indent == 0);
238
239 /* output tuples */
242 if (es->format == EXPLAIN_FORMAT_TEXT)
244 else
247
248 pfree(es->str->data);
249}
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:632
TupleDesc ExplainResultDesc(ExplainStmt *stmt)
Definition explain.c:256
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:343

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

1078{
1079 char *str;
1080
1081 /* This check is consistent with errdetail_params() */
1082 if (params == NULL || params->numParams <= 0 || maxlen == 0)
1083 return;
1084
1085 str = BuildParamLogString(params, NULL, maxlen);
1086 if (str && str[0] != '\0')
1087 ExplainPropertyText("Query Parameters", str, es);
1088}
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 1062 of file explain.c.

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

References ExplainPropertyText(), and QueryDesc::sourceText.

Referenced by explain_ExecutorEnd().

◆ ExplainResultDesc()

TupleDesc ExplainResultDesc ( ExplainStmt stmt)

Definition at line 256 of file explain.c.

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

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

4396{
4397 ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
4398}

References ExplainTargetRel(), and plan.

Referenced by ExplainNode().

◆ ExplainSubPlans()

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

Definition at line 4931 of file explain.c.

4933{
4934 ListCell *lst;
4935
4936 foreach(lst, plans)
4937 {
4939 SubPlan *sp = sps->subplan;
4940 char *cooked_plan_name;
4941
4942 /*
4943 * There can be multiple SubPlan nodes referencing the same physical
4944 * subplan (same plan_id, which is its index in PlannedStmt.subplans).
4945 * We should print a subplan only once, so track which ones we already
4946 * printed. This state must be global across the plan tree, since the
4947 * duplicate nodes could be in different plan nodes, eg both a bitmap
4948 * indexscan's indexqual and its parent heapscan's recheck qual. (We
4949 * do not worry too much about which plan node we show the subplan as
4950 * attached to in such cases.)
4951 */
4952 if (bms_is_member(sp->plan_id, es->printed_subplans))
4953 continue;
4955 sp->plan_id);
4956
4957 /*
4958 * Treat the SubPlan node as an ancestor of the plan node(s) within
4959 * it, so that ruleutils.c can find the referents of subplan
4960 * parameters.
4961 */
4962 ancestors = lcons(sp, ancestors);
4963
4964 /*
4965 * The plan has a name like exists_1 or rowcompare_2, but here we want
4966 * to prefix that with CTE, InitPlan, or SubPlan, as appropriate, for
4967 * display purposes.
4968 */
4969 if (sp->subLinkType == CTE_SUBLINK)
4970 cooked_plan_name = psprintf("CTE %s", sp->plan_name);
4971 else if (sp->isInitPlan)
4972 cooked_plan_name = psprintf("InitPlan %s", sp->plan_name);
4973 else
4974 cooked_plan_name = psprintf("SubPlan %s", sp->plan_name);
4975
4976 ExplainNode(sps->planstate, ancestors,
4978
4979 ancestors = list_delete_first(ancestors);
4980 }
4981}
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 4417 of file explain.c.

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

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

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

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

Referenced by ExplainNode().

◆ report_triggers()

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

Definition at line 1095 of file explain.c.

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

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

Referenced by ExplainPrintTriggers().

◆ show_agg_keys()

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

Definition at line 2626 of file explain.c.

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

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

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

3524{
3525 char *maxStorageType;
3527
3528 Tuplestorestate *tupstore = ctescanstate->leader->cte_table;
3529
3530 if (!es->analyze || tupstore == NULL)
3531 return;
3532
3535}
int64_t int64
Definition c.h:615
static void show_storage_info(char *maxStorageType, int64 maxSpaceUsed, ExplainState *es)
Definition explain.c:3005
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 2506 of file explain.c.

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

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

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

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

2760{
2761 Group *plan = (Group *) gstate->ss.ps.plan;
2762
2763 /* The key columns refer to the tlist of the child plan */
2764 ancestors = lcons(plan, ancestors);
2766 plan->numCols, 0, plan->grpColIdx,
2767 NULL, NULL, NULL,
2768 ancestors, es);
2769 ancestors = list_delete_first(ancestors);
2770}
Plan plan
Definition plannodes.h:1178

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

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

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

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

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

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

References appendStringInfo(), BYTES_TO_KILOBYTES, EXPLAIN_FORMAT_TEXT, ExplainIndentText(), ExplainPropertyInteger(), ExplainPropertyUInteger(), fb(), ExplainState::format, SharedHashInfo::hinstrument, i, Max, 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 3745 of file explain.c.

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

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

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

2595{
2597
2599 plan->sort.numCols, plan->nPresortedCols,
2600 plan->sort.sortColIdx,
2601 plan->sort.sortOperators, plan->sort.collations,
2602 plan->sort.nullsFirst,
2603 ancestors, es);
2604}

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

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

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

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

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

Referenced by ExplainNode(), and show_modifytable_info().

◆ show_material_info()

static void show_material_info ( MaterialState mstate,
ExplainState es 
)
static

Definition at line 3477 of file explain.c.

3478{
3479 char *maxStorageType;
3481
3482 Tuplestorestate *tupstore = mstate->tuplestorestate;
3483
3484 /*
3485 * Nothing to show if ANALYZE option wasn't used or if execution didn't
3486 * get as far as creating the tuplestore.
3487 */
3488 if (!es->analyze || tupstore == NULL)
3489 return;
3490
3493}

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

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

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

4334{
4336 mem_counters->freespace);
4338
4339 if (es->format == EXPLAIN_FORMAT_TEXT)
4340 {
4343 "Memory: used=" INT64_FORMAT "kB allocated=" INT64_FORMAT "kB",
4345 appendStringInfoChar(es->str, '\n');
4346 }
4347 else
4348 {
4349 ExplainPropertyInteger("Memory Used", "kB", memUsedkB, es);
4350 ExplainPropertyInteger("Memory Allocated", "kB", memAllocatedkB, es);
4351 }
4352}

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

2612{
2614
2615 show_sort_group_keys((PlanState *) mstate, "Sort Key",
2616 plan->numCols, 0, plan->sortColIdx,
2617 plan->sortOperators, plan->collations,
2618 plan->nullsFirst,
2619 ancestors, es);
2620}

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

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

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, Instrumentation::ntuples2, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_SELECT, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictLockStrength, ModifyTable::onConflictWhere, ModifyTable::operation, outerPlanState, PlanState::plan, ModifyTableState::ps, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_usesFdwDirectModify, show_instrumentation_count(), show_upper_qual(), PlanState::state, and ExplainState::str.

Referenced by ExplainNode().

◆ show_plan_tlist()

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

Definition at line 2448 of file explain.c.

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

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

Referenced by ExplainNode().

◆ show_qual()

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

Definition at line 2529 of file explain.c.

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

3562{
3563 char *maxStorageType,
3567
3568 if (!es->analyze)
3569 return;
3570
3571 /*
3572 * Recursive union node uses two tuplestores. We employ the storage type
3573 * from one of them which consumed more memory/disk than the other. The
3574 * storage size is sum of the two.
3575 */
3577 &tempSpaceUsed);
3579 &maxSpaceUsed);
3580
3583
3586}
Tuplestorestate * working_table
Definition execnodes.h:1580
Tuplestorestate * intermediate_table
Definition execnodes.h:1581

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

4800{
4802 int nrels = 0;
4803 int rti = -1;
4804 bool found_non_result = false;
4805 char *replacement_type = "???";
4806
4807 /* If the Result node has a subplan, it didn't replace anything. */
4808 if (result->plan.lefttree != NULL)
4809 return;
4810
4811 /* Gating result nodes should have a subplan, and we don't. */
4813
4814 switch (result->result_type)
4815 {
4816 case RESULT_TYPE_GATING:
4817 replacement_type = "Gating";
4818 break;
4819 case RESULT_TYPE_SCAN:
4820 replacement_type = "Scan";
4821 break;
4822 case RESULT_TYPE_JOIN:
4823 replacement_type = "Join";
4824 break;
4825 case RESULT_TYPE_UPPER:
4826 /* a small white lie */
4827 replacement_type = "Aggregate";
4828 break;
4829 case RESULT_TYPE_MINMAX:
4830 replacement_type = "MinMaxAggregate";
4831 break;
4832 }
4833
4834 /*
4835 * Build up a comma-separated list of user-facing names for the range
4836 * table entries in the relids set.
4837 */
4839 while ((rti = bms_next_member(result->relids, rti)) >= 0)
4840 {
4841 RangeTblEntry *rte = rt_fetch(rti, es->rtable);
4842 char *refname;
4843
4844 /*
4845 * add_outer_joins_to_relids will add join RTIs to the relids set of a
4846 * join; if that join is then replaced with a Result node, we may see
4847 * such RTIs here. But we want to completely ignore those here,
4848 * because "a LEFT JOIN b ON whatever" is a join between a and b, not
4849 * a join between a, b, and an unnamed join.
4850 */
4851 if (rte->rtekind == RTE_JOIN)
4852 continue;
4853
4854 /* Count the number of rels that aren't ignored completely. */
4855 ++nrels;
4856
4857 /* Work out what reference name to use and add it to the string. */
4858 refname = (char *) list_nth(es->rtable_names, rti - 1);
4859 if (refname == NULL)
4860 refname = rte->eref->aliasname;
4861 if (buf.len > 0)
4863 appendStringInfoString(&buf, refname);
4864
4865 /* Keep track of whether we see anything other than RTE_RESULT. */
4866 if (rte->rtekind != RTE_RESULT)
4867 found_non_result = true;
4868 }
4869
4870 /*
4871 * If this Result node is because of a single RTE that is RTE_RESULT, it
4872 * is not really replacing anything at all, because there's no other
4873 * method for implementing a scan of such an RTE, so we don't display the
4874 * Replaces line in such cases.
4875 */
4876 if (nrels <= 1 && !found_non_result &&
4877 result->result_type == RESULT_TYPE_SCAN)
4878 return;
4879
4880 /* Say what we replaced, with list of rels if available. */
4881 if (buf.len == 0)
4882 ExplainPropertyText("Replaces", replacement_type, es);
4883 else
4884 {
4885 char *s = psprintf("%s on %s", replacement_type, buf.data);
4886
4887 ExplainPropertyText("Replaces", s, es);
4888 }
4889}
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:278
@ RESULT_TYPE_SCAN
Definition plannodes.h:276
@ RESULT_TYPE_GATING
Definition plannodes.h:275
@ RESULT_TYPE_MINMAX
Definition plannodes.h:279
@ RESULT_TYPE_JOIN
Definition plannodes.h:277
ResultType result_type
Definition plannodes.h:302
Bitmapset * relids
Definition plannodes.h:304
Plan plan
Definition plannodes.h:301

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

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

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

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

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

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

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

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

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

2842{
2844 bool reverse = false;
2845 TypeCacheEntry *typentry;
2846
2847 typentry = lookup_type_cache(sortcoltype,
2849
2850 /*
2851 * Print COLLATE if it's not default for the column's type. There are
2852 * some cases where this is redundant, eg if expression is a column whose
2853 * declared collation is that collation, but it's hard to distinguish that
2854 * here (and arguably, printing COLLATE explicitly is a good idea anyway
2855 * in such cases).
2856 */
2857 if (OidIsValid(collation) && collation != get_typcollation(sortcoltype))
2858 {
2859 char *collname = get_collation_name(collation);
2860
2861 if (collname == NULL)
2862 elog(ERROR, "cache lookup failed for collation %u", collation);
2863 appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
2864 }
2865
2866 /* Print direction if not ASC, or USING if non-default sort operator */
2867 if (sortOperator == typentry->gt_opr)
2868 {
2869 appendStringInfoString(buf, " DESC");
2870 reverse = true;
2871 }
2872 else if (sortOperator != typentry->lt_opr)
2873 {
2874 char *opname = get_opname(sortOperator);
2875
2876 if (opname == NULL)
2877 elog(ERROR, "cache lookup failed for operator %u", sortOperator);
2878 appendStringInfo(buf, " USING %s", opname);
2879 /* Determine whether operator would be considered ASC or DESC */
2880 (void) get_equality_op_for_ordering_op(sortOperator, &reverse);
2881 }
2882
2883 /* Add NULLS FIRST/LAST only if it wouldn't be default */
2884 if (nullsFirst && !reverse)
2885 {
2886 appendStringInfoString(buf, " NULLS FIRST");
2887 }
2888 else if (!nullsFirst && reverse)
2889 {
2890 appendStringInfoString(buf, " NULLS LAST");
2891 }
2892}
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 3542 of file explain.c.

3543{
3544 char *maxStorageType;
3546
3547 Tuplestorestate *tupstore = tscanstate->tupstore;
3548
3549 if (!es->analyze || tupstore == NULL)
3550 return;
3551
3554}

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

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

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

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, BitmapHeapScanState::pstate, SharedBitmapHeapInstrumentation::sinstrument, BitmapHeapScanState::sinstrument, BitmapHeapScanState::stats, ExplainState::str, UINT64_FORMAT, and ExplainState::workers_state.

Referenced by ExplainNode().

◆ show_upper_qual()

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

Definition at line 2564 of file explain.c.

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

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

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

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

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

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

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

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

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

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

325{
329 BufferUsage bufusage_start,
330 bufusage;
334
335 if (es->memory)
336 {
337 /*
338 * Create a new memory context to measure planner's memory consumption
339 * accurately. Note that if the planner were to be modified to use a
340 * different memory context type, here we would be changing that to
341 * AllocSet, which might be undesirable. However, we don't have a way
342 * to create a context of the same type as another, so we pray and
343 * hope that this is OK.
344 */
346 "explain analyze planner context",
349 }
350
351 if (es->buffers)
352 bufusage_start = pgBufferUsage;
354
355 /* plan the query */
356 plan = pg_plan_query(query, queryString, cursorOptions, params, es);
357
360
361 if (es->memory)
362 {
365 }
366
367 /* calc differences of buffer counters. */
368 if (es->buffers)
369 {
370 memset(&bufusage, 0, sizeof(BufferUsage));
371 BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
372 }
373
374 /* run it (if needed) and produce output */
375 ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
376 &planduration, (es->buffers ? &bufusage : NULL),
377 es->memory ? &mem_counters : NULL);
378}
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:497
BufferUsage pgBufferUsage
Definition instrument.c:20
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition instrument.c:249
void MemoryContextMemConsumed(MemoryContext context, MemoryContextCounters *consumed)
Definition mcxt.c:835
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
PlannedStmt * pg_plan_query(Query *querytree, const char *query_string, int cursorOptions, ParamListInfo boundParams, ExplainState *es)
Definition postgres.c:887

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

Referenced by _PG_init(), and ExplainOnePlan().

◆ ExplainOneQuery_hook

ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL

Definition at line 51 of file explain.c.

Referenced by ExplainOneQuery().