LCOV - code coverage report
Current view: top level - src/backend/commands - explain.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 77.1 % 2790 2150
Test Date: 2026-01-26 10:56:24 Functions: 91.5 % 71 65
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 65.3 % 1545 1009

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * explain.c
       4                 :             :  *        Explain query execution plans
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994-5, Regents of the University of California
       8                 :             :  *
       9                 :             :  * IDENTIFICATION
      10                 :             :  *        src/backend/commands/explain.c
      11                 :             :  *
      12                 :             :  *-------------------------------------------------------------------------
      13                 :             :  */
      14                 :             : #include "postgres.h"
      15                 :             : 
      16                 :             : #include "access/xact.h"
      17                 :             : #include "catalog/pg_type.h"
      18                 :             : #include "commands/createas.h"
      19                 :             : #include "commands/defrem.h"
      20                 :             : #include "commands/explain.h"
      21                 :             : #include "commands/explain_dr.h"
      22                 :             : #include "commands/explain_format.h"
      23                 :             : #include "commands/explain_state.h"
      24                 :             : #include "commands/prepare.h"
      25                 :             : #include "foreign/fdwapi.h"
      26                 :             : #include "jit/jit.h"
      27                 :             : #include "libpq/pqformat.h"
      28                 :             : #include "libpq/protocol.h"
      29                 :             : #include "nodes/extensible.h"
      30                 :             : #include "nodes/makefuncs.h"
      31                 :             : #include "nodes/nodeFuncs.h"
      32                 :             : #include "parser/analyze.h"
      33                 :             : #include "parser/parsetree.h"
      34                 :             : #include "rewrite/rewriteHandler.h"
      35                 :             : #include "storage/bufmgr.h"
      36                 :             : #include "tcop/tcopprot.h"
      37                 :             : #include "utils/builtins.h"
      38                 :             : #include "utils/guc_tables.h"
      39                 :             : #include "utils/json.h"
      40                 :             : #include "utils/lsyscache.h"
      41                 :             : #include "utils/rel.h"
      42                 :             : #include "utils/ruleutils.h"
      43                 :             : #include "utils/snapmgr.h"
      44                 :             : #include "utils/tuplesort.h"
      45                 :             : #include "utils/typcache.h"
      46                 :             : #include "utils/xml.h"
      47                 :             : 
      48                 :             : 
      49                 :             : /* Hook for plugins to get control in ExplainOneQuery() */
      50                 :             : ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
      51                 :             : 
      52                 :             : /* Hook for plugins to get control in explain_get_index_name() */
      53                 :             : explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
      54                 :             : 
      55                 :             : /* per-plan and per-node hooks for plugins to print additional info */
      56                 :             : explain_per_plan_hook_type explain_per_plan_hook = NULL;
      57                 :             : explain_per_node_hook_type explain_per_node_hook = NULL;
      58                 :             : 
      59                 :             : /*
      60                 :             :  * Various places within need to convert bytes to kilobytes.  Round these up
      61                 :             :  * to the next whole kilobyte.
      62                 :             :  */
      63                 :             : #define BYTES_TO_KILOBYTES(b) (((b) + 1023) / 1024)
      64                 :             : 
      65                 :             : static void ExplainOneQuery(Query *query, int cursorOptions,
      66                 :             :                                                         IntoClause *into, ExplainState *es,
      67                 :             :                                                         ParseState *pstate, ParamListInfo params);
      68                 :             : static void ExplainPrintJIT(ExplainState *es, int jit_flags,
      69                 :             :                                                         JitInstrumentation *ji);
      70                 :             : static void ExplainPrintSerialize(ExplainState *es,
      71                 :             :                                                                   SerializeMetrics *metrics);
      72                 :             : static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
      73                 :             :                                                         ExplainState *es);
      74                 :             : static double elapsed_time(instr_time *starttime);
      75                 :             : static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
      76                 :             : static void ExplainNode(PlanState *planstate, List *ancestors,
      77                 :             :                                                 const char *relationship, const char *plan_name,
      78                 :             :                                                 ExplainState *es);
      79                 :             : static void show_plan_tlist(PlanState *planstate, List *ancestors,
      80                 :             :                                                         ExplainState *es);
      81                 :             : static void show_expression(Node *node, const char *qlabel,
      82                 :             :                                                         PlanState *planstate, List *ancestors,
      83                 :             :                                                         bool useprefix, ExplainState *es);
      84                 :             : static void show_qual(List *qual, const char *qlabel,
      85                 :             :                                           PlanState *planstate, List *ancestors,
      86                 :             :                                           bool useprefix, ExplainState *es);
      87                 :             : static void show_scan_qual(List *qual, const char *qlabel,
      88                 :             :                                                    PlanState *planstate, List *ancestors,
      89                 :             :                                                    ExplainState *es);
      90                 :             : static void show_upper_qual(List *qual, const char *qlabel,
      91                 :             :                                                         PlanState *planstate, List *ancestors,
      92                 :             :                                                         ExplainState *es);
      93                 :             : static void show_sort_keys(SortState *sortstate, List *ancestors,
      94                 :             :                                                    ExplainState *es);
      95                 :             : static void show_incremental_sort_keys(IncrementalSortState *incrsortstate,
      96                 :             :                                                                            List *ancestors, ExplainState *es);
      97                 :             : static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
      98                 :             :                                                                    ExplainState *es);
      99                 :             : static void show_agg_keys(AggState *astate, List *ancestors,
     100                 :             :                                                   ExplainState *es);
     101                 :             : static void show_grouping_sets(PlanState *planstate, Agg *agg,
     102                 :             :                                                            List *ancestors, ExplainState *es);
     103                 :             : static void show_grouping_set_keys(PlanState *planstate,
     104                 :             :                                                                    Agg *aggnode, Sort *sortnode,
     105                 :             :                                                                    List *context, bool useprefix,
     106                 :             :                                                                    List *ancestors, ExplainState *es);
     107                 :             : static void show_group_keys(GroupState *gstate, List *ancestors,
     108                 :             :                                                         ExplainState *es);
     109                 :             : static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
     110                 :             :                                                                  int nkeys, int nPresortedKeys, AttrNumber *keycols,
     111                 :             :                                                                  Oid *sortOperators, Oid *collations, bool *nullsFirst,
     112                 :             :                                                                  List *ancestors, ExplainState *es);
     113                 :             : static void show_sortorder_options(StringInfo buf, Node *sortexpr,
     114                 :             :                                                                    Oid sortOperator, Oid collation, bool nullsFirst);
     115                 :             : static void show_window_def(WindowAggState *planstate,
     116                 :             :                                                         List *ancestors, ExplainState *es);
     117                 :             : static void show_window_keys(StringInfo buf, PlanState *planstate,
     118                 :             :                                                          int nkeys, AttrNumber *keycols,
     119                 :             :                                                          List *ancestors, ExplainState *es);
     120                 :             : static void show_storage_info(char *maxStorageType, int64 maxSpaceUsed,
     121                 :             :                                                           ExplainState *es);
     122                 :             : static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
     123                 :             :                                                          List *ancestors, ExplainState *es);
     124                 :             : static void show_sort_info(SortState *sortstate, ExplainState *es);
     125                 :             : static void show_incremental_sort_info(IncrementalSortState *incrsortstate,
     126                 :             :                                                                            ExplainState *es);
     127                 :             : static void show_hash_info(HashState *hashstate, ExplainState *es);
     128                 :             : static void show_material_info(MaterialState *mstate, ExplainState *es);
     129                 :             : static void show_windowagg_info(WindowAggState *winstate, ExplainState *es);
     130                 :             : static void show_ctescan_info(CteScanState *ctescanstate, ExplainState *es);
     131                 :             : static void show_table_func_scan_info(TableFuncScanState *tscanstate,
     132                 :             :                                                                           ExplainState *es);
     133                 :             : static void show_recursive_union_info(RecursiveUnionState *rstate,
     134                 :             :                                                                           ExplainState *es);
     135                 :             : static void show_memoize_info(MemoizeState *mstate, List *ancestors,
     136                 :             :                                                           ExplainState *es);
     137                 :             : static void show_hashagg_info(AggState *aggstate, ExplainState *es);
     138                 :             : static void show_indexsearches_info(PlanState *planstate, ExplainState *es);
     139                 :             : static void show_tidbitmap_info(BitmapHeapScanState *planstate,
     140                 :             :                                                                 ExplainState *es);
     141                 :             : static void show_instrumentation_count(const char *qlabel, int which,
     142                 :             :                                                                            PlanState *planstate, ExplainState *es);
     143                 :             : static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
     144                 :             : static const char *explain_get_index_name(Oid indexId);
     145                 :             : static bool peek_buffer_usage(ExplainState *es, const BufferUsage *usage);
     146                 :             : static void show_buffer_usage(ExplainState *es, const BufferUsage *usage);
     147                 :             : static void show_wal_usage(ExplainState *es, const WalUsage *usage);
     148                 :             : static void show_memory_counters(ExplainState *es,
     149                 :             :                                                                  const MemoryContextCounters *mem_counters);
     150                 :             : static void show_result_replacement_info(Result *result, ExplainState *es);
     151                 :             : static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
     152                 :             :                                                                         ExplainState *es);
     153                 :             : static void ExplainScanTarget(Scan *plan, ExplainState *es);
     154                 :             : static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
     155                 :             : static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
     156                 :             : static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
     157                 :             :                                                                   ExplainState *es);
     158                 :             : static void ExplainMemberNodes(PlanState **planstates, int nplans,
     159                 :             :                                                            List *ancestors, ExplainState *es);
     160                 :             : static void ExplainMissingMembers(int nplans, int nchildren, ExplainState *es);
     161                 :             : static void ExplainSubPlans(List *plans, List *ancestors,
     162                 :             :                                                         const char *relationship, ExplainState *es);
     163                 :             : static void ExplainCustomChildren(CustomScanState *css,
     164                 :             :                                                                   List *ancestors, ExplainState *es);
     165                 :             : static ExplainWorkersState *ExplainCreateWorkersState(int num_workers);
     166                 :             : static void ExplainOpenWorker(int n, ExplainState *es);
     167                 :             : static void ExplainCloseWorker(int n, ExplainState *es);
     168                 :             : static void ExplainFlushWorkersState(ExplainState *es);
     169                 :             : 
     170                 :             : 
     171                 :             : 
     172                 :             : /*
     173                 :             :  * ExplainQuery -
     174                 :             :  *        execute an EXPLAIN command
     175                 :             :  */
     176                 :             : void
     177                 :        3613 : ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
     178                 :             :                          ParamListInfo params, DestReceiver *dest)
     179                 :             : {
     180                 :        3613 :         ExplainState *es = NewExplainState();
     181                 :        3613 :         TupOutputState *tstate;
     182                 :        3613 :         JumbleState *jstate = NULL;
     183                 :        3613 :         Query      *query;
     184                 :        3613 :         List       *rewritten;
     185                 :             : 
     186                 :             :         /* Configure the ExplainState based on the provided options */
     187                 :        3613 :         ParseExplainOptionList(es, stmt->options, pstate);
     188                 :             : 
     189                 :             :         /* Extract the query and, if enabled, jumble it */
     190                 :        3613 :         query = castNode(Query, stmt->query);
     191         [ +  + ]:        3613 :         if (IsQueryIdEnabled())
     192                 :          10 :                 jstate = JumbleQuery(query);
     193                 :             : 
     194         [ -  + ]:        3613 :         if (post_parse_analyze_hook)
     195                 :           0 :                 (*post_parse_analyze_hook) (pstate, query, jstate);
     196                 :             : 
     197                 :             :         /*
     198                 :             :          * Parse analysis was done already, but we still have to run the rule
     199                 :             :          * rewriter.  We do not do AcquireRewriteLocks: we assume the query either
     200                 :             :          * came straight from the parser, or suitable locks were acquired by
     201                 :             :          * plancache.c.
     202                 :             :          */
     203                 :        3613 :         rewritten = QueryRewrite(castNode(Query, stmt->query));
     204                 :             : 
     205                 :             :         /* emit opening boilerplate */
     206                 :        3613 :         ExplainBeginOutput(es);
     207                 :             : 
     208         [ +  - ]:        3613 :         if (rewritten == NIL)
     209                 :             :         {
     210                 :             :                 /*
     211                 :             :                  * In the case of an INSTEAD NOTHING, tell at least that.  But in
     212                 :             :                  * non-text format, the output is delimited, so this isn't necessary.
     213                 :             :                  */
     214         [ #  # ]:           0 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
     215                 :           0 :                         appendStringInfoString(es->str, "Query rewrites to nothing\n");
     216                 :           0 :         }
     217                 :             :         else
     218                 :             :         {
     219                 :        3613 :                 ListCell   *l;
     220                 :             : 
     221                 :             :                 /* Explain every plan */
     222   [ +  -  +  +  :        7247 :                 foreach(l, rewritten)
                   +  + ]
     223                 :             :                 {
     224                 :        7268 :                         ExplainOneQuery(lfirst_node(Query, l),
     225                 :        3634 :                                                         CURSOR_OPT_PARALLEL_OK, NULL, es,
     226                 :        3634 :                                                         pstate, params);
     227                 :             : 
     228                 :             :                         /* Separate plans with an appropriate separator */
     229         [ +  + ]:        3634 :                         if (lnext(rewritten, l) != NULL)
     230                 :           2 :                                 ExplainSeparatePlans(es);
     231                 :        3634 :                 }
     232                 :        3613 :         }
     233                 :             : 
     234                 :             :         /* emit closing boilerplate */
     235                 :        3613 :         ExplainEndOutput(es);
     236         [ +  - ]:        3613 :         Assert(es->indent == 0);
     237                 :             : 
     238                 :             :         /* output tuples */
     239                 :        3613 :         tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt),
     240                 :             :                                                                           &TTSOpsVirtual);
     241         [ +  + ]:        3613 :         if (es->format == EXPLAIN_FORMAT_TEXT)
     242                 :        3569 :                 do_text_output_multiline(tstate, es->str->data);
     243                 :             :         else
     244                 :          44 :                 do_text_output_oneline(tstate, es->str->data);
     245                 :        3613 :         end_tup_output(tstate);
     246                 :             : 
     247                 :        3613 :         pfree(es->str->data);
     248                 :        3613 : }
     249                 :             : 
     250                 :             : /*
     251                 :             :  * ExplainResultDesc -
     252                 :             :  *        construct the result tupledesc for an EXPLAIN
     253                 :             :  */
     254                 :             : TupleDesc
     255                 :        8593 : ExplainResultDesc(ExplainStmt *stmt)
     256                 :             : {
     257                 :        8593 :         TupleDesc       tupdesc;
     258                 :        8593 :         ListCell   *lc;
     259                 :        8593 :         Oid                     result_type = TEXTOID;
     260                 :             : 
     261                 :             :         /* Check for XML format option */
     262   [ +  +  +  +  :       17032 :         foreach(lc, stmt->options)
                   +  + ]
     263                 :             :         {
     264                 :        8439 :                 DefElem    *opt = (DefElem *) lfirst(lc);
     265                 :             : 
     266         [ +  + ]:        8439 :                 if (strcmp(opt->defname, "format") == 0)
     267                 :             :                 {
     268                 :         120 :                         char       *p = defGetString(opt);
     269                 :             : 
     270         [ +  + ]:         120 :                         if (strcmp(p, "xml") == 0)
     271                 :           3 :                                 result_type = XMLOID;
     272         [ +  + ]:         117 :                         else if (strcmp(p, "json") == 0)
     273                 :         105 :                                 result_type = JSONOID;
     274                 :             :                         else
     275                 :          12 :                                 result_type = TEXTOID;
     276                 :             :                         /* don't "break", as ExplainQuery will use the last value */
     277                 :         120 :                 }
     278                 :        8439 :         }
     279                 :             : 
     280                 :             :         /* Need a tuple descriptor representing a single TEXT or XML column */
     281                 :        8593 :         tupdesc = CreateTemplateTupleDesc(1);
     282                 :       17186 :         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
     283                 :        8593 :                                            result_type, -1, 0);
     284                 :       17186 :         return tupdesc;
     285                 :        8593 : }
     286                 :             : 
     287                 :             : /*
     288                 :             :  * ExplainOneQuery -
     289                 :             :  *        print out the execution plan for one Query
     290                 :             :  *
     291                 :             :  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
     292                 :             :  */
     293                 :             : static void
     294                 :        3660 : ExplainOneQuery(Query *query, int cursorOptions,
     295                 :             :                                 IntoClause *into, ExplainState *es,
     296                 :             :                                 ParseState *pstate, ParamListInfo params)
     297                 :             : {
     298                 :             :         /* planner will not cope with utility statements */
     299         [ +  + ]:        3660 :         if (query->commandType == CMD_UTILITY)
     300                 :             :         {
     301                 :         101 :                 ExplainOneUtility(query->utilityStmt, into, es, pstate, params);
     302                 :         101 :                 return;
     303                 :             :         }
     304                 :             : 
     305                 :             :         /* if an advisor plugin is present, let it manage things */
     306         [ -  + ]:        3559 :         if (ExplainOneQuery_hook)
     307                 :           0 :                 (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
     308                 :           0 :                                                                  pstate->p_sourcetext, params, pstate->p_queryEnv);
     309                 :             :         else
     310                 :        7118 :                 standard_ExplainOneQuery(query, cursorOptions, into, es,
     311                 :        3559 :                                                                  pstate->p_sourcetext, params, pstate->p_queryEnv);
     312                 :        3660 : }
     313                 :             : 
     314                 :             : /*
     315                 :             :  * standard_ExplainOneQuery -
     316                 :             :  *        print out the execution plan for one Query, without calling a hook.
     317                 :             :  */
     318                 :             : void
     319                 :        3552 : standard_ExplainOneQuery(Query *query, int cursorOptions,
     320                 :             :                                                  IntoClause *into, ExplainState *es,
     321                 :             :                                                  const char *queryString, ParamListInfo params,
     322                 :             :                                                  QueryEnvironment *queryEnv)
     323                 :             : {
     324                 :        3552 :         PlannedStmt *plan;
     325                 :        3552 :         instr_time      planstart,
     326                 :             :                                 planduration;
     327                 :        3552 :         BufferUsage bufusage_start,
     328                 :             :                                 bufusage;
     329                 :        3552 :         MemoryContextCounters mem_counters;
     330                 :        3552 :         MemoryContext planner_ctx = NULL;
     331                 :        3552 :         MemoryContext saved_ctx = NULL;
     332                 :             : 
     333         [ +  + ]:        3552 :         if (es->memory)
     334                 :             :         {
     335                 :             :                 /*
     336                 :             :                  * Create a new memory context to measure planner's memory consumption
     337                 :             :                  * accurately.  Note that if the planner were to be modified to use a
     338                 :             :                  * different memory context type, here we would be changing that to
     339                 :             :                  * AllocSet, which might be undesirable.  However, we don't have a way
     340                 :             :                  * to create a context of the same type as another, so we pray and
     341                 :             :                  * hope that this is OK.
     342                 :             :                  */
     343                 :           4 :                 planner_ctx = AllocSetContextCreate(CurrentMemoryContext,
     344                 :             :                                                                                         "explain analyze planner context",
     345                 :             :                                                                                         ALLOCSET_DEFAULT_SIZES);
     346                 :           4 :                 saved_ctx = MemoryContextSwitchTo(planner_ctx);
     347                 :           4 :         }
     348                 :             : 
     349         [ +  + ]:        3552 :         if (es->buffers)
     350                 :         424 :                 bufusage_start = pgBufferUsage;
     351                 :        3552 :         INSTR_TIME_SET_CURRENT(planstart);
     352                 :             : 
     353                 :             :         /* plan the query */
     354                 :        3552 :         plan = pg_plan_query(query, queryString, cursorOptions, params, es);
     355                 :             : 
     356                 :        3552 :         INSTR_TIME_SET_CURRENT(planduration);
     357                 :        3552 :         INSTR_TIME_SUBTRACT(planduration, planstart);
     358                 :             : 
     359         [ +  + ]:        3552 :         if (es->memory)
     360                 :             :         {
     361                 :           4 :                 MemoryContextSwitchTo(saved_ctx);
     362                 :           4 :                 MemoryContextMemConsumed(planner_ctx, &mem_counters);
     363                 :           4 :         }
     364                 :             : 
     365                 :             :         /* calc differences of buffer counters. */
     366         [ +  + ]:        3552 :         if (es->buffers)
     367                 :             :         {
     368                 :         424 :                 memset(&bufusage, 0, sizeof(BufferUsage));
     369                 :         424 :                 BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
     370                 :         424 :         }
     371                 :             : 
     372                 :             :         /* run it (if needed) and produce output */
     373                 :        7104 :         ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
     374         [ +  + ]:        3552 :                                    &planduration, (es->buffers ? &bufusage : NULL),
     375         [ +  + ]:        3552 :                                    es->memory ? &mem_counters : NULL);
     376                 :        3552 : }
     377                 :             : 
     378                 :             : /*
     379                 :             :  * ExplainOneUtility -
     380                 :             :  *        print out the execution plan for one utility statement
     381                 :             :  *        (In general, utility statements don't have plans, but there are some
     382                 :             :  *        we treat as special cases)
     383                 :             :  *
     384                 :             :  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
     385                 :             :  *
     386                 :             :  * This is exported because it's called back from prepare.c in the
     387                 :             :  * EXPLAIN EXECUTE case.  In that case, we'll be dealing with a statement
     388                 :             :  * that's in the plan cache, so we have to ensure we don't modify it.
     389                 :             :  */
     390                 :             : void
     391                 :          96 : ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
     392                 :             :                                   ParseState *pstate, ParamListInfo params)
     393                 :             : {
     394         [ +  - ]:          96 :         if (utilityStmt == NULL)
     395                 :           0 :                 return;
     396                 :             : 
     397         [ +  + ]:          96 :         if (IsA(utilityStmt, CreateTableAsStmt))
     398                 :             :         {
     399                 :             :                 /*
     400                 :             :                  * We have to rewrite the contained SELECT and then pass it back to
     401                 :             :                  * ExplainOneQuery.  Copy to be safe in the EXPLAIN EXECUTE case.
     402                 :             :                  */
     403                 :          22 :                 CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
     404                 :          22 :                 Query      *ctas_query;
     405                 :          22 :                 List       *rewritten;
     406                 :          22 :                 JumbleState *jstate = NULL;
     407                 :             : 
     408                 :             :                 /*
     409                 :             :                  * Check if the relation exists or not.  This is done at this stage to
     410                 :             :                  * avoid query planning or execution.
     411                 :             :                  */
     412         [ +  + ]:          22 :                 if (CreateTableAsRelExists(ctas))
     413                 :             :                 {
     414         [ +  + ]:           5 :                         if (ctas->objtype == OBJECT_TABLE)
     415                 :           3 :                                 ExplainDummyGroup("CREATE TABLE AS", NULL, es);
     416         [ +  - ]:           2 :                         else if (ctas->objtype == OBJECT_MATVIEW)
     417                 :           2 :                                 ExplainDummyGroup("CREATE MATERIALIZED VIEW", NULL, es);
     418                 :             :                         else
     419   [ #  #  #  # ]:           0 :                                 elog(ERROR, "unexpected object type: %d",
     420                 :             :                                          (int) ctas->objtype);
     421                 :           5 :                         return;
     422                 :             :                 }
     423                 :             : 
     424                 :          17 :                 ctas_query = castNode(Query, copyObject(ctas->query));
     425         [ +  + ]:          17 :                 if (IsQueryIdEnabled())
     426                 :           2 :                         jstate = JumbleQuery(ctas_query);
     427         [ +  - ]:          17 :                 if (post_parse_analyze_hook)
     428                 :           0 :                         (*post_parse_analyze_hook) (pstate, ctas_query, jstate);
     429                 :          17 :                 rewritten = QueryRewrite(ctas_query);
     430         [ +  - ]:          17 :                 Assert(list_length(rewritten) == 1);
     431                 :          34 :                 ExplainOneQuery(linitial_node(Query, rewritten),
     432                 :          17 :                                                 CURSOR_OPT_PARALLEL_OK, ctas->into, es,
     433                 :          17 :                                                 pstate, params);
     434      [ -  +  + ]:          22 :         }
     435         [ +  + ]:          74 :         else if (IsA(utilityStmt, DeclareCursorStmt))
     436                 :             :         {
     437                 :             :                 /*
     438                 :             :                  * Likewise for DECLARE CURSOR.
     439                 :             :                  *
     440                 :             :                  * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
     441                 :             :                  * actually run the query.  This is different from pre-8.3 behavior
     442                 :             :                  * but seems more useful than not running the query.  No cursor will
     443                 :             :                  * be created, however.
     444                 :             :                  */
     445                 :           9 :                 DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
     446                 :           9 :                 Query      *dcs_query;
     447                 :           9 :                 List       *rewritten;
     448                 :           9 :                 JumbleState *jstate = NULL;
     449                 :             : 
     450                 :           9 :                 dcs_query = castNode(Query, copyObject(dcs->query));
     451         [ +  + ]:           9 :                 if (IsQueryIdEnabled())
     452                 :           1 :                         jstate = JumbleQuery(dcs_query);
     453         [ +  - ]:           9 :                 if (post_parse_analyze_hook)
     454                 :           0 :                         (*post_parse_analyze_hook) (pstate, dcs_query, jstate);
     455                 :             : 
     456                 :           9 :                 rewritten = QueryRewrite(dcs_query);
     457         [ +  - ]:           9 :                 Assert(list_length(rewritten) == 1);
     458                 :          18 :                 ExplainOneQuery(linitial_node(Query, rewritten),
     459                 :           9 :                                                 dcs->options, NULL, es,
     460                 :           9 :                                                 pstate, params);
     461                 :           9 :         }
     462         [ +  - ]:          65 :         else if (IsA(utilityStmt, ExecuteStmt))
     463                 :         130 :                 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
     464                 :          65 :                                                         pstate, params);
     465         [ #  # ]:           0 :         else if (IsA(utilityStmt, NotifyStmt))
     466                 :             :         {
     467         [ #  # ]:           0 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
     468                 :           0 :                         appendStringInfoString(es->str, "NOTIFY\n");
     469                 :             :                 else
     470                 :           0 :                         ExplainDummyGroup("Notify", NULL, es);
     471                 :           0 :         }
     472                 :             :         else
     473                 :             :         {
     474         [ #  # ]:           0 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
     475                 :           0 :                         appendStringInfoString(es->str,
     476                 :             :                                                                    "Utility statements have no plan structure\n");
     477                 :             :                 else
     478                 :           0 :                         ExplainDummyGroup("Utility Statement", NULL, es);
     479                 :             :         }
     480                 :          96 : }
     481                 :             : 
     482                 :             : /*
     483                 :             :  * ExplainOnePlan -
     484                 :             :  *              given a planned query, execute it if needed, and then print
     485                 :             :  *              EXPLAIN output
     486                 :             :  *
     487                 :             :  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt,
     488                 :             :  * in which case executing the query should result in creating that table.
     489                 :             :  *
     490                 :             :  * This is exported because it's called back from prepare.c in the
     491                 :             :  * EXPLAIN EXECUTE case, and because an index advisor plugin would need
     492                 :             :  * to call it.
     493                 :             :  */
     494                 :             : void
     495                 :        3617 : ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
     496                 :             :                            const char *queryString, ParamListInfo params,
     497                 :             :                            QueryEnvironment *queryEnv, const instr_time *planduration,
     498                 :             :                            const BufferUsage *bufusage,
     499                 :             :                            const MemoryContextCounters *mem_counters)
     500                 :             : {
     501                 :        3617 :         DestReceiver *dest;
     502                 :        3617 :         QueryDesc  *queryDesc;
     503                 :        3617 :         instr_time      starttime;
     504                 :        3617 :         double          totaltime = 0;
     505                 :        3617 :         int                     eflags;
     506                 :        3617 :         int                     instrument_option = 0;
     507                 :        3617 :         SerializeMetrics serializeMetrics = {0};
     508                 :             : 
     509         [ +  - ]:        3617 :         Assert(plannedstmt->commandType != CMD_UTILITY);
     510                 :             : 
     511   [ +  +  +  + ]:        3617 :         if (es->analyze && es->timing)
     512                 :         429 :                 instrument_option |= INSTRUMENT_TIMER;
     513         [ +  + ]:        3188 :         else if (es->analyze)
     514                 :         122 :                 instrument_option |= INSTRUMENT_ROWS;
     515                 :             : 
     516         [ +  + ]:        3617 :         if (es->buffers)
     517                 :         424 :                 instrument_option |= INSTRUMENT_BUFFERS;
     518         [ +  - ]:        3617 :         if (es->wal)
     519                 :           0 :                 instrument_option |= INSTRUMENT_WAL;
     520                 :             : 
     521                 :             :         /*
     522                 :             :          * We always collect timing for the entire statement, even when node-level
     523                 :             :          * timing is off, so we don't look at es->timing here.  (We could skip
     524                 :             :          * this if !es->summary, but it's hardly worth the complication.)
     525                 :             :          */
     526                 :        3617 :         INSTR_TIME_SET_CURRENT(starttime);
     527                 :             : 
     528                 :             :         /*
     529                 :             :          * Use a snapshot with an updated command ID to ensure this query sees
     530                 :             :          * results of any previously executed queries.
     531                 :             :          */
     532                 :        3617 :         PushCopiedSnapshot(GetActiveSnapshot());
     533                 :        3617 :         UpdateActiveSnapshotCommandId();
     534                 :             : 
     535                 :             :         /*
     536                 :             :          * We discard the output if we have no use for it.  If we're explaining
     537                 :             :          * CREATE TABLE AS, we'd better use the appropriate tuple receiver, while
     538                 :             :          * the SERIALIZE option requires its own tuple receiver.  (If you specify
     539                 :             :          * SERIALIZE while explaining CREATE TABLE AS, you'll see zeroes for the
     540                 :             :          * results, which is appropriate since no data would have gone to the
     541                 :             :          * client.)
     542                 :             :          */
     543         [ +  + ]:        3617 :         if (into)
     544                 :          17 :                 dest = CreateIntoRelDestReceiver(into);
     545         [ +  + ]:        3600 :         else if (es->serialize != EXPLAIN_SERIALIZE_NONE)
     546                 :           4 :                 dest = CreateExplainSerializeDestReceiver(es);
     547                 :             :         else
     548                 :        3596 :                 dest = None_Receiver;
     549                 :             : 
     550                 :             :         /* Create a QueryDesc for the query */
     551                 :        7234 :         queryDesc = CreateQueryDesc(plannedstmt, queryString,
     552                 :        3617 :                                                                 GetActiveSnapshot(), InvalidSnapshot,
     553                 :        3617 :                                                                 dest, params, queryEnv, instrument_option);
     554                 :             : 
     555                 :             :         /* Select execution options */
     556         [ +  + ]:        3617 :         if (es->analyze)
     557                 :         551 :                 eflags = 0;                             /* default run-to-completion flags */
     558                 :             :         else
     559                 :        3066 :                 eflags = EXEC_FLAG_EXPLAIN_ONLY;
     560         [ +  + ]:        3617 :         if (es->generic)
     561                 :           2 :                 eflags |= EXEC_FLAG_EXPLAIN_GENERIC;
     562         [ +  + ]:        3617 :         if (into)
     563                 :          17 :                 eflags |= GetIntoRelEFlags(into);
     564                 :             : 
     565                 :             :         /* call ExecutorStart to prepare the plan for execution */
     566                 :        3617 :         ExecutorStart(queryDesc, eflags);
     567                 :             : 
     568                 :             :         /* Execute the plan for statistics if asked for */
     569         [ +  + ]:        3617 :         if (es->analyze)
     570                 :             :         {
     571                 :         551 :                 ScanDirection dir;
     572                 :             : 
     573                 :             :                 /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
     574   [ +  +  +  + ]:         551 :                 if (into && into->skipData)
     575                 :           4 :                         dir = NoMovementScanDirection;
     576                 :             :                 else
     577                 :         547 :                         dir = ForwardScanDirection;
     578                 :             : 
     579                 :             :                 /* run the plan */
     580                 :         551 :                 ExecutorRun(queryDesc, dir, 0);
     581                 :             : 
     582                 :             :                 /* run cleanup too */
     583                 :         551 :                 ExecutorFinish(queryDesc);
     584                 :             : 
     585                 :             :                 /* We can't run ExecutorEnd 'till we're done printing the stats... */
     586                 :         551 :                 totaltime += elapsed_time(&starttime);
     587                 :         551 :         }
     588                 :             : 
     589                 :             :         /* grab serialization metrics before we destroy the DestReceiver */
     590         [ +  + ]:        3617 :         if (es->serialize != EXPLAIN_SERIALIZE_NONE)
     591                 :           5 :                 serializeMetrics = GetSerializationMetrics(dest);
     592                 :             : 
     593                 :             :         /* call the DestReceiver's destroy method even during explain */
     594                 :        3617 :         dest->rDestroy(dest);
     595                 :             : 
     596                 :        3617 :         ExplainOpenGroup("Query", NULL, true, es);
     597                 :             : 
     598                 :             :         /* Create textual dump of plan tree */
     599                 :        3617 :         ExplainPrintPlan(es, queryDesc);
     600                 :             : 
     601                 :             :         /* Show buffer and/or memory usage in planning */
     602   [ +  +  +  + ]:        3617 :         if (peek_buffer_usage(es, bufusage) || mem_counters)
     603                 :             :         {
     604                 :         138 :                 ExplainOpenGroup("Planning", "Planning", true, es);
     605                 :             : 
     606         [ +  + ]:         138 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
     607                 :             :                 {
     608                 :          97 :                         ExplainIndentText(es);
     609                 :          97 :                         appendStringInfoString(es->str, "Planning:\n");
     610                 :          97 :                         es->indent++;
     611                 :          97 :                 }
     612                 :             : 
     613         [ +  + ]:         132 :                 if (bufusage)
     614                 :         128 :                         show_buffer_usage(es, bufusage);
     615                 :             : 
     616         [ +  + ]:         132 :                 if (mem_counters)
     617                 :           5 :                         show_memory_counters(es, mem_counters);
     618                 :             : 
     619         [ +  + ]:         132 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
     620                 :          97 :                         es->indent--;
     621                 :             : 
     622                 :         132 :                 ExplainCloseGroup("Planning", "Planning", true, es);
     623                 :         132 :         }
     624                 :             : 
     625   [ +  +  -  + ]:        3611 :         if (es->summary && planduration)
     626                 :             :         {
     627                 :         431 :                 double          plantime = INSTR_TIME_GET_DOUBLE(*planduration);
     628                 :             : 
     629                 :         431 :                 ExplainPropertyFloat("Planning Time", "ms", 1000.0 * plantime, 3, es);
     630                 :         431 :         }
     631                 :             : 
     632                 :             :         /* Print info about runtime of triggers */
     633         [ +  + ]:        3611 :         if (es->analyze)
     634                 :         550 :                 ExplainPrintTriggers(es, queryDesc);
     635                 :             : 
     636                 :             :         /*
     637                 :             :          * Print info about JITing. Tied to es->costs because we don't want to
     638                 :             :          * display this in regression tests, as it'd cause output differences
     639                 :             :          * depending on build options.  Might want to separate that out from COSTS
     640                 :             :          * at a later stage.
     641                 :             :          */
     642         [ +  + ]:        3611 :         if (es->costs)
     643                 :        1312 :                 ExplainPrintJITSummary(es, queryDesc);
     644                 :             : 
     645                 :             :         /* Print info about serialization of output */
     646         [ +  + ]:        3611 :         if (es->serialize != EXPLAIN_SERIALIZE_NONE)
     647                 :           5 :                 ExplainPrintSerialize(es, &serializeMetrics);
     648                 :             : 
     649                 :             :         /* Allow plugins to print additional information */
     650         [ +  + ]:        3611 :         if (explain_per_plan_hook)
     651                 :        7220 :                 (*explain_per_plan_hook) (plannedstmt, into, es, queryString,
     652                 :        3610 :                                                                   params, queryEnv);
     653                 :             : 
     654                 :             :         /*
     655                 :             :          * Close down the query and free resources.  Include time for this in the
     656                 :             :          * total execution time (although it should be pretty minimal).
     657                 :             :          */
     658                 :        3611 :         INSTR_TIME_SET_CURRENT(starttime);
     659                 :             : 
     660                 :        3611 :         ExecutorEnd(queryDesc);
     661                 :             : 
     662                 :        3611 :         FreeQueryDesc(queryDesc);
     663                 :             : 
     664                 :        3611 :         PopActiveSnapshot();
     665                 :             : 
     666                 :             :         /* We need a CCI just in case query expanded to multiple plans */
     667         [ +  + ]:        3611 :         if (es->analyze)
     668                 :         550 :                 CommandCounterIncrement();
     669                 :             : 
     670                 :        3611 :         totaltime += elapsed_time(&starttime);
     671                 :             : 
     672                 :             :         /*
     673                 :             :          * We only report execution time if we actually ran the query (that is,
     674                 :             :          * the user specified ANALYZE), and if summary reporting is enabled (the
     675                 :             :          * user can set SUMMARY OFF to not have the timing information included in
     676                 :             :          * the output).  By default, ANALYZE sets SUMMARY to true.
     677                 :             :          */
     678   [ +  +  +  + ]:        3611 :         if (es->summary && es->analyze)
     679                 :         860 :                 ExplainPropertyFloat("Execution Time", "ms", 1000.0 * totaltime, 3,
     680                 :         430 :                                                          es);
     681                 :             : 
     682                 :        3611 :         ExplainCloseGroup("Query", NULL, true, es);
     683                 :        3611 : }
     684                 :             : 
     685                 :             : /*
     686                 :             :  * ExplainPrintSettings -
     687                 :             :  *    Print summary of modified settings affecting query planning.
     688                 :             :  */
     689                 :             : static void
     690                 :        3610 : ExplainPrintSettings(ExplainState *es)
     691                 :             : {
     692                 :        3610 :         int                     num;
     693                 :        3610 :         struct config_generic **gucs;
     694                 :             : 
     695                 :             :         /* bail out if information about settings not requested */
     696         [ +  + ]:        3610 :         if (!es->settings)
     697                 :        3608 :                 return;
     698                 :             : 
     699                 :             :         /* request an array of relevant settings */
     700                 :           2 :         gucs = get_explain_guc_options(&num);
     701                 :             : 
     702         [ +  + ]:           2 :         if (es->format != EXPLAIN_FORMAT_TEXT)
     703                 :             :         {
     704                 :           1 :                 ExplainOpenGroup("Settings", "Settings", true, es);
     705                 :             : 
     706         [ +  + ]:           3 :                 for (int i = 0; i < num; i++)
     707                 :             :                 {
     708                 :           2 :                         char       *setting;
     709                 :           2 :                         struct config_generic *conf = gucs[i];
     710                 :             : 
     711                 :           2 :                         setting = GetConfigOptionByName(conf->name, NULL, true);
     712                 :             : 
     713                 :           2 :                         ExplainPropertyText(conf->name, setting, es);
     714                 :           2 :                 }
     715                 :             : 
     716                 :           1 :                 ExplainCloseGroup("Settings", "Settings", true, es);
     717                 :           1 :         }
     718                 :             :         else
     719                 :             :         {
     720                 :           1 :                 StringInfoData str;
     721                 :             : 
     722                 :             :                 /* In TEXT mode, print nothing if there are no options */
     723         [ -  + ]:           1 :                 if (num <= 0)
     724                 :           0 :                         return;
     725                 :             : 
     726                 :           1 :                 initStringInfo(&str);
     727                 :             : 
     728         [ +  + ]:           3 :                 for (int i = 0; i < num; i++)
     729                 :             :                 {
     730                 :           2 :                         char       *setting;
     731                 :           2 :                         struct config_generic *conf = gucs[i];
     732                 :             : 
     733         [ +  + ]:           2 :                         if (i > 0)
     734                 :           1 :                                 appendStringInfoString(&str, ", ");
     735                 :             : 
     736                 :           2 :                         setting = GetConfigOptionByName(conf->name, NULL, true);
     737                 :             : 
     738         [ +  - ]:           2 :                         if (setting)
     739                 :           2 :                                 appendStringInfo(&str, "%s = '%s'", conf->name, setting);
     740                 :             :                         else
     741                 :           0 :                                 appendStringInfo(&str, "%s = NULL", conf->name);
     742                 :           2 :                 }
     743                 :             : 
     744                 :           1 :                 ExplainPropertyText("Settings", str.data, es);
     745         [ -  + ]:           1 :         }
     746         [ -  + ]:        3610 : }
     747                 :             : 
     748                 :             : /*
     749                 :             :  * ExplainPrintPlan -
     750                 :             :  *        convert a QueryDesc's plan tree to text and append it to es->str
     751                 :             :  *
     752                 :             :  * The caller should have set up the options fields of *es, as well as
     753                 :             :  * initializing the output buffer es->str.  Also, output formatting state
     754                 :             :  * such as the indent level is assumed valid.  Plan-tree-specific fields
     755                 :             :  * in *es are initialized here.
     756                 :             :  *
     757                 :             :  * NB: will not work on utility statements
     758                 :             :  */
     759                 :             : void
     760                 :        3610 : ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
     761                 :             : {
     762                 :        3610 :         Bitmapset  *rels_used = NULL;
     763                 :        3610 :         PlanState  *ps;
     764                 :        3610 :         ListCell   *lc;
     765                 :             : 
     766                 :             :         /* Set up ExplainState fields associated with this plan tree */
     767         [ +  - ]:        3610 :         Assert(queryDesc->plannedstmt != NULL);
     768                 :        3610 :         es->pstmt = queryDesc->plannedstmt;
     769                 :        3610 :         es->rtable = queryDesc->plannedstmt->rtable;
     770                 :        3610 :         ExplainPreScanNode(queryDesc->planstate, &rels_used);
     771                 :        3610 :         es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
     772                 :        7220 :         es->deparse_cxt = deparse_context_for_plan_tree(queryDesc->plannedstmt,
     773                 :        3610 :                                                                                                         es->rtable_names);
     774                 :        3610 :         es->printed_subplans = NULL;
     775                 :        3610 :         es->rtable_size = list_length(es->rtable);
     776   [ +  -  +  +  :       13460 :         foreach(lc, es->rtable)
                   +  + ]
     777                 :             :         {
     778                 :        9850 :                 RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
     779                 :             : 
     780         [ +  + ]:        9850 :                 if (rte->rtekind == RTE_GROUP)
     781                 :             :                 {
     782                 :         292 :                         es->rtable_size--;
     783                 :         292 :                         break;
     784                 :             :                 }
     785         [ +  + ]:        9850 :         }
     786                 :             : 
     787                 :             :         /*
     788                 :             :          * Sometimes we mark a Gather node as "invisible", which means that it's
     789                 :             :          * not to be displayed in EXPLAIN output.  The purpose of this is to allow
     790                 :             :          * running regression tests with debug_parallel_query=regress to get the
     791                 :             :          * same results as running the same tests with debug_parallel_query=off.
     792                 :             :          * Such marking is currently only supported on a Gather at the top of the
     793                 :             :          * plan.  We skip that node, and we must also hide per-worker detail data
     794                 :             :          * further down in the plan tree.
     795                 :             :          */
     796                 :        3610 :         ps = queryDesc->planstate;
     797   [ +  +  +  - ]:        3610 :         if (IsA(ps, GatherState) && ((Gather *) ps->plan)->invisible)
     798                 :             :         {
     799                 :           0 :                 ps = outerPlanState(ps);
     800                 :           0 :                 es->hide_workers = true;
     801                 :           0 :         }
     802                 :        3610 :         ExplainNode(ps, NIL, NULL, NULL, es);
     803                 :             : 
     804                 :             :         /*
     805                 :             :          * If requested, include information about GUC parameters with values that
     806                 :             :          * don't match the built-in defaults.
     807                 :             :          */
     808                 :        3610 :         ExplainPrintSettings(es);
     809                 :             : 
     810                 :             :         /*
     811                 :             :          * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_AUTO, but we don't show
     812                 :             :          * the queryid in any of the EXPLAIN plans to keep stable the results
     813                 :             :          * generated by regression test suites.
     814                 :             :          */
     815   [ +  +  +  +  :        3610 :         if (es->verbose && queryDesc->plannedstmt->queryId != INT64CONST(0) &&
                   -  + ]
     816                 :           3 :                 compute_query_id != COMPUTE_QUERY_ID_REGRESS)
     817                 :             :         {
     818                 :           3 :                 ExplainPropertyInteger("Query Identifier", NULL,
     819                 :           3 :                                                            queryDesc->plannedstmt->queryId, es);
     820                 :           3 :         }
     821                 :        3610 : }
     822                 :             : 
     823                 :             : /*
     824                 :             :  * ExplainPrintTriggers -
     825                 :             :  *        convert a QueryDesc's trigger statistics to text and append it to
     826                 :             :  *        es->str
     827                 :             :  *
     828                 :             :  * The caller should have set up the options fields of *es, as well as
     829                 :             :  * initializing the output buffer es->str.  Other fields in *es are
     830                 :             :  * initialized here.
     831                 :             :  */
     832                 :             : void
     833                 :         550 : ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
     834                 :             : {
     835                 :         550 :         ResultRelInfo *rInfo;
     836                 :         550 :         bool            show_relname;
     837                 :         550 :         List       *resultrels;
     838                 :         550 :         List       *routerels;
     839                 :         550 :         List       *targrels;
     840                 :         550 :         ListCell   *l;
     841                 :             : 
     842                 :         550 :         resultrels = queryDesc->estate->es_opened_result_relations;
     843                 :         550 :         routerels = queryDesc->estate->es_tuple_routing_result_relations;
     844                 :         550 :         targrels = queryDesc->estate->es_trig_target_relations;
     845                 :             : 
     846                 :         550 :         ExplainOpenGroup("Triggers", "Triggers", false, es);
     847                 :             : 
     848         [ +  + ]:        1098 :         show_relname = (list_length(resultrels) > 1 ||
     849         [ -  + ]:         548 :                                         routerels != NIL || targrels != NIL);
     850   [ +  +  +  +  :         568 :         foreach(l, resultrels)
                   +  + ]
     851                 :             :         {
     852                 :          18 :                 rInfo = (ResultRelInfo *) lfirst(l);
     853                 :          18 :                 report_triggers(rInfo, show_relname, es);
     854                 :          18 :         }
     855                 :             : 
     856   [ -  +  #  #  :         550 :         foreach(l, routerels)
                   -  + ]
     857                 :             :         {
     858                 :           0 :                 rInfo = (ResultRelInfo *) lfirst(l);
     859                 :           0 :                 report_triggers(rInfo, show_relname, es);
     860                 :           0 :         }
     861                 :             : 
     862   [ -  +  #  #  :         550 :         foreach(l, targrels)
                   -  + ]
     863                 :             :         {
     864                 :           0 :                 rInfo = (ResultRelInfo *) lfirst(l);
     865                 :           0 :                 report_triggers(rInfo, show_relname, es);
     866                 :           0 :         }
     867                 :             : 
     868                 :         550 :         ExplainCloseGroup("Triggers", "Triggers", false, es);
     869                 :         550 : }
     870                 :             : 
     871                 :             : /*
     872                 :             :  * ExplainPrintJITSummary -
     873                 :             :  *    Print summarized JIT instrumentation from leader and workers
     874                 :             :  */
     875                 :             : void
     876                 :        1312 : ExplainPrintJITSummary(ExplainState *es, QueryDesc *queryDesc)
     877                 :             : {
     878                 :        1312 :         JitInstrumentation ji = {0};
     879                 :             : 
     880         [ -  + ]:        1312 :         if (!(queryDesc->estate->es_jit_flags & PGJIT_PERFORM))
     881                 :        1312 :                 return;
     882                 :             : 
     883                 :             :         /*
     884                 :             :          * Work with a copy instead of modifying the leader state, since this
     885                 :             :          * function may be called twice
     886                 :             :          */
     887         [ #  # ]:           0 :         if (queryDesc->estate->es_jit)
     888                 :           0 :                 InstrJitAgg(&ji, &queryDesc->estate->es_jit->instr);
     889                 :             : 
     890                 :             :         /* If this process has done JIT in parallel workers, merge stats */
     891         [ #  # ]:           0 :         if (queryDesc->estate->es_jit_worker_instr)
     892                 :           0 :                 InstrJitAgg(&ji, queryDesc->estate->es_jit_worker_instr);
     893                 :             : 
     894                 :           0 :         ExplainPrintJIT(es, queryDesc->estate->es_jit_flags, &ji);
     895         [ -  + ]:        1312 : }
     896                 :             : 
     897                 :             : /*
     898                 :             :  * ExplainPrintJIT -
     899                 :             :  *        Append information about JITing to es->str.
     900                 :             :  */
     901                 :             : static void
     902                 :           0 : ExplainPrintJIT(ExplainState *es, int jit_flags, JitInstrumentation *ji)
     903                 :             : {
     904                 :           0 :         instr_time      total_time;
     905                 :             : 
     906                 :             :         /* don't print information if no JITing happened */
     907   [ #  #  #  # ]:           0 :         if (!ji || ji->created_functions == 0)
     908                 :           0 :                 return;
     909                 :             : 
     910                 :             :         /* calculate total time */
     911                 :           0 :         INSTR_TIME_SET_ZERO(total_time);
     912                 :             :         /* don't add deform_counter, it's included in generation_counter */
     913                 :           0 :         INSTR_TIME_ADD(total_time, ji->generation_counter);
     914                 :           0 :         INSTR_TIME_ADD(total_time, ji->inlining_counter);
     915                 :           0 :         INSTR_TIME_ADD(total_time, ji->optimization_counter);
     916                 :           0 :         INSTR_TIME_ADD(total_time, ji->emission_counter);
     917                 :             : 
     918                 :           0 :         ExplainOpenGroup("JIT", "JIT", true, es);
     919                 :             : 
     920                 :             :         /* for higher density, open code the text output format */
     921         [ #  # ]:           0 :         if (es->format == EXPLAIN_FORMAT_TEXT)
     922                 :             :         {
     923                 :           0 :                 ExplainIndentText(es);
     924                 :           0 :                 appendStringInfoString(es->str, "JIT:\n");
     925                 :           0 :                 es->indent++;
     926                 :             : 
     927                 :           0 :                 ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
     928                 :             : 
     929                 :           0 :                 ExplainIndentText(es);
     930                 :           0 :                 appendStringInfo(es->str, "Options: %s %s, %s %s, %s %s, %s %s\n",
     931                 :           0 :                                                  "Inlining", jit_flags & PGJIT_INLINE ? "true" : "false",
     932                 :           0 :                                                  "Optimization", jit_flags & PGJIT_OPT3 ? "true" : "false",
     933                 :           0 :                                                  "Expressions", jit_flags & PGJIT_EXPR ? "true" : "false",
     934                 :           0 :                                                  "Deforming", jit_flags & PGJIT_DEFORM ? "true" : "false");
     935                 :             : 
     936   [ #  #  #  # ]:           0 :                 if (es->analyze && es->timing)
     937                 :             :                 {
     938                 :           0 :                         ExplainIndentText(es);
     939                 :           0 :                         appendStringInfo(es->str,
     940                 :             :                                                          "Timing: %s %.3f ms (%s %.3f ms), %s %.3f ms, %s %.3f ms, %s %.3f ms, %s %.3f ms\n",
     941                 :           0 :                                                          "Generation", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->generation_counter),
     942                 :           0 :                                                          "Deform", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->deform_counter),
     943                 :           0 :                                                          "Inlining", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->inlining_counter),
     944                 :           0 :                                                          "Optimization", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->optimization_counter),
     945                 :           0 :                                                          "Emission", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->emission_counter),
     946                 :           0 :                                                          "Total", 1000.0 * INSTR_TIME_GET_DOUBLE(total_time));
     947                 :           0 :                 }
     948                 :             : 
     949                 :           0 :                 es->indent--;
     950                 :           0 :         }
     951                 :             :         else
     952                 :             :         {
     953                 :           0 :                 ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
     954                 :             : 
     955                 :           0 :                 ExplainOpenGroup("Options", "Options", true, es);
     956                 :           0 :                 ExplainPropertyBool("Inlining", jit_flags & PGJIT_INLINE, es);
     957                 :           0 :                 ExplainPropertyBool("Optimization", jit_flags & PGJIT_OPT3, es);
     958                 :           0 :                 ExplainPropertyBool("Expressions", jit_flags & PGJIT_EXPR, es);
     959                 :           0 :                 ExplainPropertyBool("Deforming", jit_flags & PGJIT_DEFORM, es);
     960                 :           0 :                 ExplainCloseGroup("Options", "Options", true, es);
     961                 :             : 
     962   [ #  #  #  # ]:           0 :                 if (es->analyze && es->timing)
     963                 :             :                 {
     964                 :           0 :                         ExplainOpenGroup("Timing", "Timing", true, es);
     965                 :             : 
     966                 :           0 :                         ExplainOpenGroup("Generation", "Generation", true, es);
     967                 :           0 :                         ExplainPropertyFloat("Deform", "ms",
     968                 :           0 :                                                                  1000.0 * INSTR_TIME_GET_DOUBLE(ji->deform_counter),
     969                 :           0 :                                                                  3, es);
     970                 :           0 :                         ExplainPropertyFloat("Total", "ms",
     971                 :           0 :                                                                  1000.0 * INSTR_TIME_GET_DOUBLE(ji->generation_counter),
     972                 :           0 :                                                                  3, es);
     973                 :           0 :                         ExplainCloseGroup("Generation", "Generation", true, es);
     974                 :             : 
     975                 :           0 :                         ExplainPropertyFloat("Inlining", "ms",
     976                 :           0 :                                                                  1000.0 * INSTR_TIME_GET_DOUBLE(ji->inlining_counter),
     977                 :           0 :                                                                  3, es);
     978                 :           0 :                         ExplainPropertyFloat("Optimization", "ms",
     979                 :           0 :                                                                  1000.0 * INSTR_TIME_GET_DOUBLE(ji->optimization_counter),
     980                 :           0 :                                                                  3, es);
     981                 :           0 :                         ExplainPropertyFloat("Emission", "ms",
     982                 :           0 :                                                                  1000.0 * INSTR_TIME_GET_DOUBLE(ji->emission_counter),
     983                 :           0 :                                                                  3, es);
     984                 :           0 :                         ExplainPropertyFloat("Total", "ms",
     985                 :           0 :                                                                  1000.0 * INSTR_TIME_GET_DOUBLE(total_time),
     986                 :           0 :                                                                  3, es);
     987                 :             : 
     988                 :           0 :                         ExplainCloseGroup("Timing", "Timing", true, es);
     989                 :           0 :                 }
     990                 :             :         }
     991                 :             : 
     992                 :           0 :         ExplainCloseGroup("JIT", "JIT", true, es);
     993         [ #  # ]:           0 : }
     994                 :             : 
     995                 :             : /*
     996                 :             :  * ExplainPrintSerialize -
     997                 :             :  *        Append information about query output volume to es->str.
     998                 :             :  */
     999                 :             : static void
    1000                 :           5 : ExplainPrintSerialize(ExplainState *es, SerializeMetrics *metrics)
    1001                 :             : {
    1002                 :           5 :         const char *format;
    1003                 :             : 
    1004                 :             :         /* We shouldn't get called for EXPLAIN_SERIALIZE_NONE */
    1005         [ +  + ]:           5 :         if (es->serialize == EXPLAIN_SERIALIZE_TEXT)
    1006                 :           4 :                 format = "text";
    1007                 :             :         else
    1008                 :             :         {
    1009         [ +  - ]:           1 :                 Assert(es->serialize == EXPLAIN_SERIALIZE_BINARY);
    1010                 :           1 :                 format = "binary";
    1011                 :             :         }
    1012                 :             : 
    1013                 :           5 :         ExplainOpenGroup("Serialization", "Serialization", true, es);
    1014                 :             : 
    1015         [ +  + ]:           5 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    1016                 :             :         {
    1017                 :           4 :                 ExplainIndentText(es);
    1018         [ +  + ]:           4 :                 if (es->timing)
    1019                 :           6 :                         appendStringInfo(es->str, "Serialization: time=%.3f ms  output=" UINT64_FORMAT "kB  format=%s\n",
    1020                 :           3 :                                                          1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
    1021                 :           3 :                                                          BYTES_TO_KILOBYTES(metrics->bytesSent),
    1022                 :           3 :                                                          format);
    1023                 :             :                 else
    1024                 :           2 :                         appendStringInfo(es->str, "Serialization: output=" UINT64_FORMAT "kB  format=%s\n",
    1025                 :           1 :                                                          BYTES_TO_KILOBYTES(metrics->bytesSent),
    1026                 :           1 :                                                          format);
    1027                 :             : 
    1028   [ +  +  +  - ]:           4 :                 if (es->buffers && peek_buffer_usage(es, &metrics->bufferUsage))
    1029                 :             :                 {
    1030                 :           0 :                         es->indent++;
    1031                 :           0 :                         show_buffer_usage(es, &metrics->bufferUsage);
    1032                 :           0 :                         es->indent--;
    1033                 :           0 :                 }
    1034                 :           4 :         }
    1035                 :             :         else
    1036                 :             :         {
    1037         [ -  + ]:           1 :                 if (es->timing)
    1038                 :           1 :                         ExplainPropertyFloat("Time", "ms",
    1039                 :           1 :                                                                  1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
    1040                 :           1 :                                                                  3, es);
    1041                 :           1 :                 ExplainPropertyUInteger("Output Volume", "kB",
    1042                 :           1 :                                                                 BYTES_TO_KILOBYTES(metrics->bytesSent), es);
    1043                 :           1 :                 ExplainPropertyText("Format", format, es);
    1044         [ -  + ]:           1 :                 if (es->buffers)
    1045                 :           1 :                         show_buffer_usage(es, &metrics->bufferUsage);
    1046                 :             :         }
    1047                 :             : 
    1048                 :           5 :         ExplainCloseGroup("Serialization", "Serialization", true, es);
    1049                 :           5 : }
    1050                 :             : 
    1051                 :             : /*
    1052                 :             :  * ExplainQueryText -
    1053                 :             :  *        add a "Query Text" node that contains the actual text of the query
    1054                 :             :  *
    1055                 :             :  * The caller should have set up the options fields of *es, as well as
    1056                 :             :  * initializing the output buffer es->str.
    1057                 :             :  *
    1058                 :             :  */
    1059                 :             : void
    1060                 :           0 : ExplainQueryText(ExplainState *es, QueryDesc *queryDesc)
    1061                 :             : {
    1062         [ #  # ]:           0 :         if (queryDesc->sourceText)
    1063                 :           0 :                 ExplainPropertyText("Query Text", queryDesc->sourceText, es);
    1064                 :           0 : }
    1065                 :             : 
    1066                 :             : /*
    1067                 :             :  * ExplainQueryParameters -
    1068                 :             :  *        add a "Query Parameters" node that describes the parameters of the query
    1069                 :             :  *
    1070                 :             :  * The caller should have set up the options fields of *es, as well as
    1071                 :             :  * initializing the output buffer es->str.
    1072                 :             :  *
    1073                 :             :  */
    1074                 :             : void
    1075                 :           0 : ExplainQueryParameters(ExplainState *es, ParamListInfo params, int maxlen)
    1076                 :             : {
    1077                 :           0 :         char       *str;
    1078                 :             : 
    1079                 :             :         /* This check is consistent with errdetail_params() */
    1080   [ #  #  #  #  :           0 :         if (params == NULL || params->numParams <= 0 || maxlen == 0)
                   #  # ]
    1081                 :           0 :                 return;
    1082                 :             : 
    1083                 :           0 :         str = BuildParamLogString(params, NULL, maxlen);
    1084   [ #  #  #  # ]:           0 :         if (str && str[0] != '\0')
    1085                 :           0 :                 ExplainPropertyText("Query Parameters", str, es);
    1086         [ #  # ]:           0 : }
    1087                 :             : 
    1088                 :             : /*
    1089                 :             :  * report_triggers -
    1090                 :             :  *              report execution stats for a single relation's triggers
    1091                 :             :  */
    1092                 :             : static void
    1093                 :          18 : report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
    1094                 :             : {
    1095                 :          18 :         int                     nt;
    1096                 :             : 
    1097   [ -  +  #  # ]:          18 :         if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
    1098                 :          18 :                 return;
    1099         [ #  # ]:           0 :         for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
    1100                 :             :         {
    1101                 :           0 :                 Trigger    *trig = rInfo->ri_TrigDesc->triggers + nt;
    1102                 :           0 :                 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
    1103                 :           0 :                 char       *relname;
    1104                 :           0 :                 char       *conname = NULL;
    1105                 :             : 
    1106                 :             :                 /* Must clean up instrumentation state */
    1107                 :           0 :                 InstrEndLoop(instr);
    1108                 :             : 
    1109                 :             :                 /*
    1110                 :             :                  * We ignore triggers that were never invoked; they likely aren't
    1111                 :             :                  * relevant to the current query type.
    1112                 :             :                  */
    1113         [ #  # ]:           0 :                 if (instr->ntuples == 0)
    1114                 :           0 :                         continue;
    1115                 :             : 
    1116                 :           0 :                 ExplainOpenGroup("Trigger", NULL, true, es);
    1117                 :             : 
    1118                 :           0 :                 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
    1119         [ #  # ]:           0 :                 if (OidIsValid(trig->tgconstraint))
    1120                 :           0 :                         conname = get_constraint_name(trig->tgconstraint);
    1121                 :             : 
    1122                 :             :                 /*
    1123                 :             :                  * In text format, we avoid printing both the trigger name and the
    1124                 :             :                  * constraint name unless VERBOSE is specified.  In non-text formats
    1125                 :             :                  * we just print everything.
    1126                 :             :                  */
    1127         [ #  # ]:           0 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    1128                 :             :                 {
    1129   [ #  #  #  # ]:           0 :                         if (es->verbose || conname == NULL)
    1130                 :           0 :                                 appendStringInfo(es->str, "Trigger %s", trig->tgname);
    1131                 :             :                         else
    1132                 :           0 :                                 appendStringInfoString(es->str, "Trigger");
    1133         [ #  # ]:           0 :                         if (conname)
    1134                 :           0 :                                 appendStringInfo(es->str, " for constraint %s", conname);
    1135         [ #  # ]:           0 :                         if (show_relname)
    1136                 :           0 :                                 appendStringInfo(es->str, " on %s", relname);
    1137         [ #  # ]:           0 :                         if (es->timing)
    1138                 :           0 :                                 appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
    1139                 :           0 :                                                                  INSTR_TIME_GET_MILLISEC(instr->total),
    1140                 :           0 :                                                                  instr->ntuples);
    1141                 :             :                         else
    1142                 :           0 :                                 appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
    1143                 :           0 :                 }
    1144                 :             :                 else
    1145                 :             :                 {
    1146                 :           0 :                         ExplainPropertyText("Trigger Name", trig->tgname, es);
    1147         [ #  # ]:           0 :                         if (conname)
    1148                 :           0 :                                 ExplainPropertyText("Constraint Name", conname, es);
    1149                 :           0 :                         ExplainPropertyText("Relation", relname, es);
    1150         [ #  # ]:           0 :                         if (es->timing)
    1151                 :           0 :                                 ExplainPropertyFloat("Time", "ms",
    1152                 :           0 :                                                                          INSTR_TIME_GET_MILLISEC(instr->total), 3,
    1153                 :           0 :                                                                          es);
    1154                 :           0 :                         ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
    1155                 :             :                 }
    1156                 :             : 
    1157         [ #  # ]:           0 :                 if (conname)
    1158                 :           0 :                         pfree(conname);
    1159                 :             : 
    1160                 :           0 :                 ExplainCloseGroup("Trigger", NULL, true, es);
    1161         [ #  # ]:           0 :         }
    1162                 :          18 : }
    1163                 :             : 
    1164                 :             : /* Compute elapsed time in seconds since given timestamp */
    1165                 :             : static double
    1166                 :        4160 : elapsed_time(instr_time *starttime)
    1167                 :             : {
    1168                 :        4160 :         instr_time      endtime;
    1169                 :             : 
    1170                 :        4160 :         INSTR_TIME_SET_CURRENT(endtime);
    1171                 :        4160 :         INSTR_TIME_SUBTRACT(endtime, *starttime);
    1172                 :        8320 :         return INSTR_TIME_GET_DOUBLE(endtime);
    1173                 :        4160 : }
    1174                 :             : 
    1175                 :             : /*
    1176                 :             :  * ExplainPreScanNode -
    1177                 :             :  *        Prescan the planstate tree to identify which RTEs are referenced
    1178                 :             :  *
    1179                 :             :  * Adds the relid of each referenced RTE to *rels_used.  The result controls
    1180                 :             :  * which RTEs are assigned aliases by select_rtable_names_for_explain.
    1181                 :             :  * This ensures that we don't confusingly assign un-suffixed aliases to RTEs
    1182                 :             :  * that never appear in the EXPLAIN output (such as inheritance parents).
    1183                 :             :  */
    1184                 :             : static bool
    1185                 :       13402 : ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
    1186                 :             : {
    1187                 :       13402 :         Plan       *plan = planstate->plan;
    1188                 :             : 
    1189   [ -  -  +  +  :       13402 :         switch (nodeTag(plan))
             +  +  +  + ]
    1190                 :             :         {
    1191                 :             :                 case T_SeqScan:
    1192                 :             :                 case T_SampleScan:
    1193                 :             :                 case T_IndexScan:
    1194                 :             :                 case T_IndexOnlyScan:
    1195                 :             :                 case T_BitmapHeapScan:
    1196                 :             :                 case T_TidScan:
    1197                 :             :                 case T_TidRangeScan:
    1198                 :             :                 case T_SubqueryScan:
    1199                 :             :                 case T_FunctionScan:
    1200                 :             :                 case T_TableFuncScan:
    1201                 :             :                 case T_ValuesScan:
    1202                 :             :                 case T_CteScan:
    1203                 :             :                 case T_NamedTuplestoreScan:
    1204                 :             :                 case T_WorkTableScan:
    1205                 :       12528 :                         *rels_used = bms_add_member(*rels_used,
    1206                 :        6264 :                                                                                 ((Scan *) plan)->scanrelid);
    1207                 :        6264 :                         break;
    1208                 :             :                 case T_ForeignScan:
    1209                 :           0 :                         *rels_used = bms_add_members(*rels_used,
    1210                 :           0 :                                                                                  ((ForeignScan *) plan)->fs_base_relids);
    1211                 :           0 :                         break;
    1212                 :             :                 case T_CustomScan:
    1213                 :           0 :                         *rels_used = bms_add_members(*rels_used,
    1214                 :           0 :                                                                                  ((CustomScan *) plan)->custom_relids);
    1215                 :           0 :                         break;
    1216                 :             :                 case T_ModifyTable:
    1217                 :         284 :                         *rels_used = bms_add_member(*rels_used,
    1218                 :         142 :                                                                                 ((ModifyTable *) plan)->nominalRelation);
    1219         [ +  + ]:         142 :                         if (((ModifyTable *) plan)->exclRelRTI)
    1220                 :          32 :                                 *rels_used = bms_add_member(*rels_used,
    1221                 :          16 :                                                                                         ((ModifyTable *) plan)->exclRelRTI);
    1222                 :             :                         /* Ensure Vars used in RETURNING will have refnames */
    1223         [ +  + ]:         142 :                         if (plan->targetlist)
    1224                 :          64 :                                 *rels_used = bms_add_member(*rels_used,
    1225                 :          32 :                                                                                         linitial_int(((ModifyTable *) plan)->resultRelations));
    1226                 :         142 :                         break;
    1227                 :             :                 case T_Append:
    1228                 :        1166 :                         *rels_used = bms_add_members(*rels_used,
    1229                 :         583 :                                                                                  ((Append *) plan)->apprelids);
    1230                 :         583 :                         break;
    1231                 :             :                 case T_MergeAppend:
    1232                 :         112 :                         *rels_used = bms_add_members(*rels_used,
    1233                 :          56 :                                                                                  ((MergeAppend *) plan)->apprelids);
    1234                 :          56 :                         break;
    1235                 :             :                 case T_Result:
    1236                 :         866 :                         *rels_used = bms_add_members(*rels_used,
    1237                 :         433 :                                                                                  ((Result *) plan)->relids);
    1238                 :         433 :                         break;
    1239                 :             :                 default:
    1240                 :        5924 :                         break;
    1241                 :             :         }
    1242                 :             : 
    1243                 :       26804 :         return planstate_tree_walker(planstate, ExplainPreScanNode, rels_used);
    1244                 :       13402 : }
    1245                 :             : 
    1246                 :             : /*
    1247                 :             :  * plan_is_disabled
    1248                 :             :  *              Checks if the given plan node type was disabled during query planning.
    1249                 :             :  *              This is evident by the disabled_nodes field being higher than the sum of
    1250                 :             :  *              the disabled_nodes field from the plan's children.
    1251                 :             :  */
    1252                 :             : static bool
    1253                 :       13372 : plan_is_disabled(Plan *plan)
    1254                 :             : {
    1255                 :       13372 :         int                     child_disabled_nodes;
    1256                 :             : 
    1257                 :             :         /* The node is certainly not disabled if this is zero */
    1258         [ +  + ]:       13372 :         if (plan->disabled_nodes == 0)
    1259                 :       13309 :                 return false;
    1260                 :             : 
    1261                 :          63 :         child_disabled_nodes = 0;
    1262                 :             : 
    1263                 :             :         /*
    1264                 :             :          * Handle special nodes first.  Children of BitmapOrs and BitmapAnds can't
    1265                 :             :          * be disabled, so no need to handle those specifically.
    1266                 :             :          */
    1267         [ +  + ]:          63 :         if (IsA(plan, Append))
    1268                 :             :         {
    1269                 :           1 :                 ListCell   *lc;
    1270                 :           1 :                 Append     *aplan = (Append *) plan;
    1271                 :             : 
    1272                 :             :                 /*
    1273                 :             :                  * Sum the Append childrens' disabled_nodes.  This purposefully
    1274                 :             :                  * includes any run-time pruned children.  Ignoring those could give
    1275                 :             :                  * us the incorrect number of disabled nodes.
    1276                 :             :                  */
    1277   [ +  -  +  +  :           4 :                 foreach(lc, aplan->appendplans)
                   +  + ]
    1278                 :             :                 {
    1279                 :           3 :                         Plan       *subplan = lfirst(lc);
    1280                 :             : 
    1281                 :           3 :                         child_disabled_nodes += subplan->disabled_nodes;
    1282                 :           3 :                 }
    1283                 :           1 :         }
    1284         [ +  + ]:          62 :         else if (IsA(plan, MergeAppend))
    1285                 :             :         {
    1286                 :           1 :                 ListCell   *lc;
    1287                 :           1 :                 MergeAppend *maplan = (MergeAppend *) plan;
    1288                 :             : 
    1289                 :             :                 /*
    1290                 :             :                  * Sum the MergeAppend childrens' disabled_nodes.  This purposefully
    1291                 :             :                  * includes any run-time pruned children.  Ignoring those could give
    1292                 :             :                  * us the incorrect number of disabled nodes.
    1293                 :             :                  */
    1294   [ +  -  +  +  :           5 :                 foreach(lc, maplan->mergeplans)
                   +  + ]
    1295                 :             :                 {
    1296                 :           4 :                         Plan       *subplan = lfirst(lc);
    1297                 :             : 
    1298                 :           4 :                         child_disabled_nodes += subplan->disabled_nodes;
    1299                 :           4 :                 }
    1300                 :           1 :         }
    1301         [ -  + ]:          61 :         else if (IsA(plan, SubqueryScan))
    1302                 :           0 :                 child_disabled_nodes += ((SubqueryScan *) plan)->subplan->disabled_nodes;
    1303         [ -  + ]:          61 :         else if (IsA(plan, CustomScan))
    1304                 :             :         {
    1305                 :           0 :                 ListCell   *lc;
    1306                 :           0 :                 CustomScan *cplan = (CustomScan *) plan;
    1307                 :             : 
    1308   [ #  #  #  #  :           0 :                 foreach(lc, cplan->custom_plans)
                   #  # ]
    1309                 :             :                 {
    1310                 :           0 :                         Plan       *subplan = lfirst(lc);
    1311                 :             : 
    1312                 :           0 :                         child_disabled_nodes += subplan->disabled_nodes;
    1313                 :           0 :                 }
    1314                 :           0 :         }
    1315                 :             :         else
    1316                 :             :         {
    1317                 :             :                 /*
    1318                 :             :                  * Else, sum up disabled_nodes from the plan's inner and outer side.
    1319                 :             :                  */
    1320         [ +  + ]:          61 :                 if (outerPlan(plan))
    1321                 :          38 :                         child_disabled_nodes += outerPlan(plan)->disabled_nodes;
    1322         [ +  + ]:          61 :                 if (innerPlan(plan))
    1323                 :          18 :                         child_disabled_nodes += innerPlan(plan)->disabled_nodes;
    1324                 :             :         }
    1325                 :             : 
    1326                 :             :         /*
    1327                 :             :          * It's disabled if the plan's disabled_nodes is higher than the sum of
    1328                 :             :          * its child's plan disabled_nodes.
    1329                 :             :          */
    1330         [ +  + ]:          63 :         if (plan->disabled_nodes > child_disabled_nodes)
    1331                 :          37 :                 return true;
    1332                 :             : 
    1333                 :          26 :         return false;
    1334                 :       13372 : }
    1335                 :             : 
    1336                 :             : /*
    1337                 :             :  * ExplainNode -
    1338                 :             :  *        Appends a description of a plan tree to es->str
    1339                 :             :  *
    1340                 :             :  * planstate points to the executor state node for the current plan node.
    1341                 :             :  * We need to work from a PlanState node, not just a Plan node, in order to
    1342                 :             :  * get at the instrumentation data (if any) as well as the list of subplans.
    1343                 :             :  *
    1344                 :             :  * ancestors is a list of parent Plan and SubPlan nodes, most-closely-nested
    1345                 :             :  * first.  These are needed in order to interpret PARAM_EXEC Params.
    1346                 :             :  *
    1347                 :             :  * relationship describes the relationship of this plan node to its parent
    1348                 :             :  * (eg, "Outer", "Inner"); it can be null at top level.  plan_name is an
    1349                 :             :  * optional name to be attached to the node.
    1350                 :             :  *
    1351                 :             :  * In text format, es->indent is controlled in this function since we only
    1352                 :             :  * want it to change at plan-node boundaries (but a few subroutines will
    1353                 :             :  * transiently increment it).  In non-text formats, es->indent corresponds
    1354                 :             :  * to the nesting depth of logical output groups, and therefore is controlled
    1355                 :             :  * by ExplainOpenGroup/ExplainCloseGroup.
    1356                 :             :  */
    1357                 :             : static void
    1358                 :       13372 : ExplainNode(PlanState *planstate, List *ancestors,
    1359                 :             :                         const char *relationship, const char *plan_name,
    1360                 :             :                         ExplainState *es)
    1361                 :             : {
    1362                 :       13372 :         Plan       *plan = planstate->plan;
    1363                 :       13372 :         const char *pname;                      /* node type name for text output */
    1364                 :       13372 :         const char *sname;                      /* node type name for non-text output */
    1365                 :       13372 :         const char *strategy = NULL;
    1366                 :       13372 :         const char *partialmode = NULL;
    1367                 :       13372 :         const char *operation = NULL;
    1368                 :       13372 :         const char *custom_name = NULL;
    1369                 :       13372 :         ExplainWorkersState *save_workers_state = es->workers_state;
    1370                 :       13372 :         int                     save_indent = es->indent;
    1371                 :       13372 :         bool            haschildren;
    1372                 :       13372 :         bool            isdisabled;
    1373                 :             : 
    1374                 :             :         /*
    1375                 :             :          * Prepare per-worker output buffers, if needed.  We'll append the data in
    1376                 :             :          * these to the main output string further down.
    1377                 :             :          */
    1378   [ +  +  +  -  :       13372 :         if (planstate->worker_instrument && es->analyze && !es->hide_workers)
                   -  + ]
    1379                 :         171 :                 es->workers_state = ExplainCreateWorkersState(planstate->worker_instrument->num_workers);
    1380                 :             :         else
    1381                 :       13201 :                 es->workers_state = NULL;
    1382                 :             : 
    1383                 :             :         /* Identify plan node type, and print generic details */
    1384   [ +  +  +  +  :       13372 :         switch (nodeTag(plan))
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  -  +  
          +  +  +  -  -  
             +  +  +  + ]
    1385                 :             :         {
    1386                 :             :                 case T_Result:
    1387                 :         426 :                         pname = sname = "Result";
    1388                 :         426 :                         break;
    1389                 :             :                 case T_ProjectSet:
    1390                 :          37 :                         pname = sname = "ProjectSet";
    1391                 :          37 :                         break;
    1392                 :             :                 case T_ModifyTable:
    1393                 :         142 :                         sname = "ModifyTable";
    1394   [ -  +  +  +  :         142 :                         switch (((ModifyTable *) plan)->operation)
                      + ]
    1395                 :             :                         {
    1396                 :             :                                 case CMD_INSERT:
    1397                 :          33 :                                         pname = operation = "Insert";
    1398                 :          33 :                                         break;
    1399                 :             :                                 case CMD_UPDATE:
    1400                 :          58 :                                         pname = operation = "Update";
    1401                 :          58 :                                         break;
    1402                 :             :                                 case CMD_DELETE:
    1403                 :          21 :                                         pname = operation = "Delete";
    1404                 :          21 :                                         break;
    1405                 :             :                                 case CMD_MERGE:
    1406                 :          30 :                                         pname = operation = "Merge";
    1407                 :          30 :                                         break;
    1408                 :             :                                 default:
    1409                 :           0 :                                         pname = "???";
    1410                 :           0 :                                         break;
    1411                 :             :                         }
    1412                 :         142 :                         break;
    1413                 :             :                 case T_Append:
    1414                 :         577 :                         pname = sname = "Append";
    1415                 :         577 :                         break;
    1416                 :             :                 case T_MergeAppend:
    1417                 :          56 :                         pname = sname = "Merge Append";
    1418                 :          56 :                         break;
    1419                 :             :                 case T_RecursiveUnion:
    1420                 :           9 :                         pname = sname = "Recursive Union";
    1421                 :           9 :                         break;
    1422                 :             :                 case T_BitmapAnd:
    1423                 :           7 :                         pname = sname = "BitmapAnd";
    1424                 :           7 :                         break;
    1425                 :             :                 case T_BitmapOr:
    1426                 :          24 :                         pname = sname = "BitmapOr";
    1427                 :          24 :                         break;
    1428                 :             :                 case T_NestLoop:
    1429                 :         618 :                         pname = sname = "Nested Loop";
    1430                 :         618 :                         break;
    1431                 :             :                 case T_MergeJoin:
    1432                 :         104 :                         pname = "Merge";      /* "Join" gets added by jointype switch */
    1433                 :         104 :                         sname = "Merge Join";
    1434                 :         104 :                         break;
    1435                 :             :                 case T_HashJoin:
    1436                 :         627 :                         pname = "Hash";               /* "Join" gets added by jointype switch */
    1437                 :         627 :                         sname = "Hash Join";
    1438                 :         627 :                         break;
    1439                 :             :                 case T_SeqScan:
    1440                 :        4238 :                         pname = sname = "Seq Scan";
    1441                 :        4238 :                         break;
    1442                 :             :                 case T_SampleScan:
    1443                 :          18 :                         pname = sname = "Sample Scan";
    1444                 :          18 :                         break;
    1445                 :             :                 case T_Gather:
    1446                 :         116 :                         pname = sname = "Gather";
    1447                 :         116 :                         break;
    1448                 :             :                 case T_GatherMerge:
    1449                 :          46 :                         pname = sname = "Gather Merge";
    1450                 :          46 :                         break;
    1451                 :             :                 case T_IndexScan:
    1452                 :         535 :                         pname = sname = "Index Scan";
    1453                 :         535 :                         break;
    1454                 :             :                 case T_IndexOnlyScan:
    1455                 :         404 :                         pname = sname = "Index Only Scan";
    1456                 :         404 :                         break;
    1457                 :             :                 case T_BitmapIndexScan:
    1458                 :         705 :                         pname = sname = "Bitmap Index Scan";
    1459                 :         705 :                         break;
    1460                 :             :                 case T_BitmapHeapScan:
    1461                 :         673 :                         pname = sname = "Bitmap Heap Scan";
    1462                 :         673 :                         break;
    1463                 :             :                 case T_TidScan:
    1464                 :          13 :                         pname = sname = "Tid Scan";
    1465                 :          13 :                         break;
    1466                 :             :                 case T_TidRangeScan:
    1467                 :          21 :                         pname = sname = "Tid Range Scan";
    1468                 :          21 :                         break;
    1469                 :             :                 case T_SubqueryScan:
    1470                 :          73 :                         pname = sname = "Subquery Scan";
    1471                 :          73 :                         break;
    1472                 :             :                 case T_FunctionScan:
    1473                 :         109 :                         pname = sname = "Function Scan";
    1474                 :         109 :                         break;
    1475                 :             :                 case T_TableFuncScan:
    1476                 :          13 :                         pname = sname = "Table Function Scan";
    1477                 :          13 :                         break;
    1478                 :             :                 case T_ValuesScan:
    1479                 :          98 :                         pname = sname = "Values Scan";
    1480                 :          98 :                         break;
    1481                 :             :                 case T_CteScan:
    1482                 :          41 :                         pname = sname = "CTE Scan";
    1483                 :          41 :                         break;
    1484                 :             :                 case T_NamedTuplestoreScan:
    1485                 :           4 :                         pname = sname = "Named Tuplestore Scan";
    1486                 :           4 :                         break;
    1487                 :             :                 case T_WorkTableScan:
    1488                 :           9 :                         pname = sname = "WorkTable Scan";
    1489                 :           9 :                         break;
    1490                 :             :                 case T_ForeignScan:
    1491                 :           0 :                         sname = "Foreign Scan";
    1492   [ #  #  #  #  :           0 :                         switch (((ForeignScan *) plan)->operation)
                      # ]
    1493                 :             :                         {
    1494                 :             :                                 case CMD_SELECT:
    1495                 :           0 :                                         pname = "Foreign Scan";
    1496                 :           0 :                                         operation = "Select";
    1497                 :           0 :                                         break;
    1498                 :             :                                 case CMD_INSERT:
    1499                 :           0 :                                         pname = "Foreign Insert";
    1500                 :           0 :                                         operation = "Insert";
    1501                 :           0 :                                         break;
    1502                 :             :                                 case CMD_UPDATE:
    1503                 :           0 :                                         pname = "Foreign Update";
    1504                 :           0 :                                         operation = "Update";
    1505                 :           0 :                                         break;
    1506                 :             :                                 case CMD_DELETE:
    1507                 :           0 :                                         pname = "Foreign Delete";
    1508                 :           0 :                                         operation = "Delete";
    1509                 :           0 :                                         break;
    1510                 :             :                                 default:
    1511                 :           0 :                                         pname = "???";
    1512                 :           0 :                                         break;
    1513                 :             :                         }
    1514                 :           0 :                         break;
    1515                 :             :                 case T_CustomScan:
    1516                 :           0 :                         sname = "Custom Scan";
    1517                 :           0 :                         custom_name = ((CustomScan *) plan)->methods->CustomName;
    1518         [ #  # ]:           0 :                         if (custom_name)
    1519                 :           0 :                                 pname = psprintf("Custom Scan (%s)", custom_name);
    1520                 :             :                         else
    1521                 :           0 :                                 pname = sname;
    1522                 :           0 :                         break;
    1523                 :             :                 case T_Material:
    1524                 :         179 :                         pname = sname = "Materialize";
    1525                 :         179 :                         break;
    1526                 :             :                 case T_Memoize:
    1527                 :          41 :                         pname = sname = "Memoize";
    1528                 :          41 :                         break;
    1529                 :             :                 case T_Sort:
    1530                 :         659 :                         pname = sname = "Sort";
    1531                 :         659 :                         break;
    1532                 :             :                 case T_IncrementalSort:
    1533                 :          64 :                         pname = sname = "Incremental Sort";
    1534                 :          64 :                         break;
    1535                 :             :                 case T_Group:
    1536                 :          16 :                         pname = sname = "Group";
    1537                 :          16 :                         break;
    1538                 :             :                 case T_Agg:
    1539                 :             :                         {
    1540                 :        1715 :                                 Agg                *agg = (Agg *) plan;
    1541                 :             : 
    1542                 :        1715 :                                 sname = "Aggregate";
    1543   [ -  +  +  +  :        1715 :                                 switch (agg->aggstrategy)
                      + ]
    1544                 :             :                                 {
    1545                 :             :                                         case AGG_PLAIN:
    1546                 :        1219 :                                                 pname = "Aggregate";
    1547                 :        1219 :                                                 strategy = "Plain";
    1548                 :        1219 :                                                 break;
    1549                 :             :                                         case AGG_SORTED:
    1550                 :         102 :                                                 pname = "GroupAggregate";
    1551                 :         102 :                                                 strategy = "Sorted";
    1552                 :         102 :                                                 break;
    1553                 :             :                                         case AGG_HASHED:
    1554                 :         376 :                                                 pname = "HashAggregate";
    1555                 :         376 :                                                 strategy = "Hashed";
    1556                 :         376 :                                                 break;
    1557                 :             :                                         case AGG_MIXED:
    1558                 :          18 :                                                 pname = "MixedAggregate";
    1559                 :          18 :                                                 strategy = "Mixed";
    1560                 :          18 :                                                 break;
    1561                 :             :                                         default:
    1562                 :           0 :                                                 pname = "Aggregate ???";
    1563                 :           0 :                                                 strategy = "???";
    1564                 :           0 :                                                 break;
    1565                 :             :                                 }
    1566                 :             : 
    1567         [ +  + ]:        1715 :                                 if (DO_AGGSPLIT_SKIPFINAL(agg->aggsplit))
    1568                 :             :                                 {
    1569                 :         163 :                                         partialmode = "Partial";
    1570                 :         163 :                                         pname = psprintf("%s %s", partialmode, pname);
    1571                 :         163 :                                 }
    1572         [ +  + ]:        1552 :                                 else if (DO_AGGSPLIT_COMBINE(agg->aggsplit))
    1573                 :             :                                 {
    1574                 :         122 :                                         partialmode = "Finalize";
    1575                 :         122 :                                         pname = psprintf("%s %s", partialmode, pname);
    1576                 :         122 :                                 }
    1577                 :             :                                 else
    1578                 :        1430 :                                         partialmode = "Simple";
    1579                 :        1715 :                         }
    1580                 :        1715 :                         break;
    1581                 :             :                 case T_WindowAgg:
    1582                 :          77 :                         pname = sname = "WindowAgg";
    1583                 :          77 :                         break;
    1584                 :             :                 case T_Unique:
    1585                 :          70 :                         pname = sname = "Unique";
    1586                 :          70 :                         break;
    1587                 :             :                 case T_SetOp:
    1588                 :          19 :                         sname = "SetOp";
    1589      [ -  +  + ]:          19 :                         switch (((SetOp *) plan)->strategy)
    1590                 :             :                         {
    1591                 :             :                                 case SETOP_SORTED:
    1592                 :          11 :                                         pname = "SetOp";
    1593                 :          11 :                                         strategy = "Sorted";
    1594                 :          11 :                                         break;
    1595                 :             :                                 case SETOP_HASHED:
    1596                 :           8 :                                         pname = "HashSetOp";
    1597                 :           8 :                                         strategy = "Hashed";
    1598                 :           8 :                                         break;
    1599                 :             :                                 default:
    1600                 :           0 :                                         pname = "SetOp ???";
    1601                 :           0 :                                         strategy = "???";
    1602                 :           0 :                                         break;
    1603                 :             :                         }
    1604                 :          19 :                         break;
    1605                 :             :                 case T_LockRows:
    1606                 :           5 :                         pname = sname = "LockRows";
    1607                 :           5 :                         break;
    1608                 :             :                 case T_Limit:
    1609                 :         157 :                         pname = sname = "Limit";
    1610                 :         157 :                         break;
    1611                 :             :                 case T_Hash:
    1612                 :         627 :                         pname = sname = "Hash";
    1613                 :         627 :                         break;
    1614                 :             :                 default:
    1615                 :           0 :                         pname = sname = "???";
    1616                 :           0 :                         break;
    1617                 :             :         }
    1618                 :             : 
    1619                 :       13372 :         ExplainOpenGroup("Plan",
    1620                 :       13372 :                                          relationship ? NULL : "Plan",
    1621                 :       13372 :                                          true, es);
    1622                 :             : 
    1623         [ +  + ]:       13372 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    1624                 :             :         {
    1625         [ +  + ]:       13199 :                 if (plan_name)
    1626                 :             :                 {
    1627                 :         235 :                         ExplainIndentText(es);
    1628                 :         235 :                         appendStringInfo(es->str, "%s\n", plan_name);
    1629                 :         235 :                         es->indent++;
    1630                 :         235 :                 }
    1631         [ +  + ]:       13199 :                 if (es->indent)
    1632                 :             :                 {
    1633                 :        9633 :                         ExplainIndentText(es);
    1634                 :        9633 :                         appendStringInfoString(es->str, "->  ");
    1635                 :        9633 :                         es->indent += 2;
    1636                 :        9633 :                 }
    1637         [ +  + ]:       13199 :                 if (plan->parallel_aware)
    1638                 :         264 :                         appendStringInfoString(es->str, "Parallel ");
    1639         [ +  - ]:       13199 :                 if (plan->async_capable)
    1640                 :           0 :                         appendStringInfoString(es->str, "Async ");
    1641                 :       13199 :                 appendStringInfoString(es->str, pname);
    1642                 :       13199 :                 es->indent++;
    1643                 :       13199 :         }
    1644                 :             :         else
    1645                 :             :         {
    1646                 :         173 :                 ExplainPropertyText("Node Type", sname, es);
    1647         [ +  + ]:         173 :                 if (strategy)
    1648                 :          26 :                         ExplainPropertyText("Strategy", strategy, es);
    1649         [ +  + ]:         173 :                 if (partialmode)
    1650                 :          26 :                         ExplainPropertyText("Partial Mode", partialmode, es);
    1651         [ +  + ]:         173 :                 if (operation)
    1652                 :           1 :                         ExplainPropertyText("Operation", operation, es);
    1653         [ +  + ]:         173 :                 if (relationship)
    1654                 :         129 :                         ExplainPropertyText("Parent Relationship", relationship, es);
    1655         [ +  - ]:         173 :                 if (plan_name)
    1656                 :           0 :                         ExplainPropertyText("Subplan Name", plan_name, es);
    1657         [ +  - ]:         173 :                 if (custom_name)
    1658                 :           0 :                         ExplainPropertyText("Custom Plan Provider", custom_name, es);
    1659                 :         173 :                 ExplainPropertyBool("Parallel Aware", plan->parallel_aware, es);
    1660                 :         173 :                 ExplainPropertyBool("Async Capable", plan->async_capable, es);
    1661                 :             :         }
    1662                 :             : 
    1663   [ +  +  +  +  :       13372 :         switch (nodeTag(plan))
             +  +  -  +  
                      + ]
    1664                 :             :         {
    1665                 :             :                 case T_SeqScan:
    1666                 :             :                 case T_SampleScan:
    1667                 :             :                 case T_BitmapHeapScan:
    1668                 :             :                 case T_TidScan:
    1669                 :             :                 case T_TidRangeScan:
    1670                 :             :                 case T_SubqueryScan:
    1671                 :             :                 case T_FunctionScan:
    1672                 :             :                 case T_TableFuncScan:
    1673                 :             :                 case T_ValuesScan:
    1674                 :             :                 case T_CteScan:
    1675                 :             :                 case T_WorkTableScan:
    1676                 :        5306 :                         ExplainScanTarget((Scan *) plan, es);
    1677                 :        5306 :                         break;
    1678                 :             :                 case T_ForeignScan:
    1679                 :             :                 case T_CustomScan:
    1680         [ #  # ]:           0 :                         if (((Scan *) plan)->scanrelid > 0)
    1681                 :           0 :                                 ExplainScanTarget((Scan *) plan, es);
    1682                 :           0 :                         break;
    1683                 :             :                 case T_IndexScan:
    1684                 :             :                         {
    1685                 :         535 :                                 IndexScan  *indexscan = (IndexScan *) plan;
    1686                 :             : 
    1687                 :        1070 :                                 ExplainIndexScanDetails(indexscan->indexid,
    1688                 :         535 :                                                                                 indexscan->indexorderdir,
    1689                 :         535 :                                                                                 es);
    1690                 :         535 :                                 ExplainScanTarget((Scan *) indexscan, es);
    1691                 :         535 :                         }
    1692                 :         535 :                         break;
    1693                 :             :                 case T_IndexOnlyScan:
    1694                 :             :                         {
    1695                 :         404 :                                 IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
    1696                 :             : 
    1697                 :         808 :                                 ExplainIndexScanDetails(indexonlyscan->indexid,
    1698                 :         404 :                                                                                 indexonlyscan->indexorderdir,
    1699                 :         404 :                                                                                 es);
    1700                 :         404 :                                 ExplainScanTarget((Scan *) indexonlyscan, es);
    1701                 :         404 :                         }
    1702                 :         404 :                         break;
    1703                 :             :                 case T_BitmapIndexScan:
    1704                 :             :                         {
    1705                 :         705 :                                 BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
    1706                 :        1410 :                                 const char *indexname =
    1707                 :         705 :                                         explain_get_index_name(bitmapindexscan->indexid);
    1708                 :             : 
    1709         [ +  + ]:         705 :                                 if (es->format == EXPLAIN_FORMAT_TEXT)
    1710                 :        1390 :                                         appendStringInfo(es->str, " on %s",
    1711                 :         695 :                                                                          quote_identifier(indexname));
    1712                 :             :                                 else
    1713                 :          10 :                                         ExplainPropertyText("Index Name", indexname, es);
    1714                 :         705 :                         }
    1715                 :         705 :                         break;
    1716                 :             :                 case T_ModifyTable:
    1717                 :         142 :                         ExplainModifyTarget((ModifyTable *) plan, es);
    1718                 :         142 :                         break;
    1719                 :             :                 case T_NestLoop:
    1720                 :             :                 case T_MergeJoin:
    1721                 :             :                 case T_HashJoin:
    1722                 :             :                         {
    1723                 :        1349 :                                 const char *jointype;
    1724                 :             : 
    1725   [ +  +  +  +  :        1349 :                                 switch (((Join *) plan)->jointype)
             +  +  +  -  
                      + ]
    1726                 :             :                                 {
    1727                 :             :                                         case JOIN_INNER:
    1728                 :         797 :                                                 jointype = "Inner";
    1729                 :         797 :                                                 break;
    1730                 :             :                                         case JOIN_LEFT:
    1731                 :         283 :                                                 jointype = "Left";
    1732                 :         283 :                                                 break;
    1733                 :             :                                         case JOIN_FULL:
    1734                 :          83 :                                                 jointype = "Full";
    1735                 :          83 :                                                 break;
    1736                 :             :                                         case JOIN_RIGHT:
    1737                 :          89 :                                                 jointype = "Right";
    1738                 :          89 :                                                 break;
    1739                 :             :                                         case JOIN_SEMI:
    1740                 :          45 :                                                 jointype = "Semi";
    1741                 :          45 :                                                 break;
    1742                 :             :                                         case JOIN_ANTI:
    1743                 :          15 :                                                 jointype = "Anti";
    1744                 :          15 :                                                 break;
    1745                 :             :                                         case JOIN_RIGHT_SEMI:
    1746                 :          19 :                                                 jointype = "Right Semi";
    1747                 :          19 :                                                 break;
    1748                 :             :                                         case JOIN_RIGHT_ANTI:
    1749                 :          18 :                                                 jointype = "Right Anti";
    1750                 :          18 :                                                 break;
    1751                 :             :                                         default:
    1752                 :           0 :                                                 jointype = "???";
    1753                 :           0 :                                                 break;
    1754                 :             :                                 }
    1755         [ +  + ]:        1349 :                                 if (es->format == EXPLAIN_FORMAT_TEXT)
    1756                 :             :                                 {
    1757                 :             :                                         /*
    1758                 :             :                                          * For historical reasons, the join type is interpolated
    1759                 :             :                                          * into the node type name...
    1760                 :             :                                          */
    1761         [ +  + ]:        1326 :                                         if (((Join *) plan)->jointype != JOIN_INNER)
    1762                 :         547 :                                                 appendStringInfo(es->str, " %s Join", jointype);
    1763         [ +  + ]:         779 :                                         else if (!IsA(plan, NestLoop))
    1764                 :         378 :                                                 appendStringInfoString(es->str, " Join");
    1765                 :        1326 :                                 }
    1766                 :             :                                 else
    1767                 :          23 :                                         ExplainPropertyText("Join Type", jointype, es);
    1768                 :        1349 :                         }
    1769                 :        1349 :                         break;
    1770                 :             :                 case T_SetOp:
    1771                 :             :                         {
    1772                 :          19 :                                 const char *setopcmd;
    1773                 :             : 
    1774   [ -  +  -  +  :          19 :                                 switch (((SetOp *) plan)->cmd)
                      + ]
    1775                 :             :                                 {
    1776                 :             :                                         case SETOPCMD_INTERSECT:
    1777                 :           9 :                                                 setopcmd = "Intersect";
    1778                 :           9 :                                                 break;
    1779                 :             :                                         case SETOPCMD_INTERSECT_ALL:
    1780                 :           0 :                                                 setopcmd = "Intersect All";
    1781                 :           0 :                                                 break;
    1782                 :             :                                         case SETOPCMD_EXCEPT:
    1783                 :           9 :                                                 setopcmd = "Except";
    1784                 :           9 :                                                 break;
    1785                 :             :                                         case SETOPCMD_EXCEPT_ALL:
    1786                 :           1 :                                                 setopcmd = "Except All";
    1787                 :           1 :                                                 break;
    1788                 :             :                                         default:
    1789                 :           0 :                                                 setopcmd = "???";
    1790                 :           0 :                                                 break;
    1791                 :             :                                 }
    1792         [ +  - ]:          19 :                                 if (es->format == EXPLAIN_FORMAT_TEXT)
    1793                 :          19 :                                         appendStringInfo(es->str, " %s", setopcmd);
    1794                 :             :                                 else
    1795                 :           0 :                                         ExplainPropertyText("Command", setopcmd, es);
    1796                 :          19 :                         }
    1797                 :          19 :                         break;
    1798                 :             :                 default:
    1799                 :        4912 :                         break;
    1800                 :             :         }
    1801                 :             : 
    1802         [ +  + ]:       13372 :         if (es->costs)
    1803                 :             :         {
    1804         [ +  + ]:        2875 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    1805                 :             :                 {
    1806                 :        5450 :                         appendStringInfo(es->str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
    1807                 :        2725 :                                                          plan->startup_cost, plan->total_cost,
    1808                 :        2725 :                                                          plan->plan_rows, plan->plan_width);
    1809                 :        2725 :                 }
    1810                 :             :                 else
    1811                 :             :                 {
    1812                 :         300 :                         ExplainPropertyFloat("Startup Cost", NULL, plan->startup_cost,
    1813                 :         150 :                                                                  2, es);
    1814                 :         300 :                         ExplainPropertyFloat("Total Cost", NULL, plan->total_cost,
    1815                 :         150 :                                                                  2, es);
    1816                 :         300 :                         ExplainPropertyFloat("Plan Rows", NULL, plan->plan_rows,
    1817                 :         150 :                                                                  0, es);
    1818                 :         300 :                         ExplainPropertyInteger("Plan Width", NULL, plan->plan_width,
    1819                 :         150 :                                                                    es);
    1820                 :             :                 }
    1821                 :        2875 :         }
    1822                 :             : 
    1823                 :             :         /*
    1824                 :             :          * We have to forcibly clean up the instrumentation state because we
    1825                 :             :          * haven't done ExecutorEnd yet.  This is pretty grotty ...
    1826                 :             :          *
    1827                 :             :          * Note: contrib/auto_explain could cause instrumentation to be set up
    1828                 :             :          * even though we didn't ask for it here.  Be careful not to print any
    1829                 :             :          * instrumentation results the user didn't ask for.  But we do the
    1830                 :             :          * InstrEndLoop call anyway, if possible, to reduce the number of cases
    1831                 :             :          * auto_explain has to contend with.
    1832                 :             :          */
    1833         [ +  + ]:       13372 :         if (planstate->instrument)
    1834                 :        1306 :                 InstrEndLoop(planstate->instrument);
    1835                 :             : 
    1836         [ +  + ]:       13372 :         if (es->analyze &&
    1837   [ +  -  +  + ]:        1304 :                 planstate->instrument && planstate->instrument->nloops > 0)
    1838                 :             :         {
    1839                 :        1181 :                 double          nloops = planstate->instrument->nloops;
    1840                 :        1181 :                 double          startup_ms = INSTR_TIME_GET_MILLISEC(planstate->instrument->startup) / nloops;
    1841                 :        1181 :                 double          total_ms = INSTR_TIME_GET_MILLISEC(planstate->instrument->total) / nloops;
    1842                 :        1181 :                 double          rows = planstate->instrument->ntuples / nloops;
    1843                 :             : 
    1844         [ +  + ]:        1181 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    1845                 :             :                 {
    1846                 :        1018 :                         appendStringInfoString(es->str, " (actual ");
    1847                 :             : 
    1848         [ +  + ]:        1018 :                         if (es->timing)
    1849                 :         556 :                                 appendStringInfo(es->str, "time=%.3f..%.3f ", startup_ms, total_ms);
    1850                 :             : 
    1851                 :        1018 :                         appendStringInfo(es->str, "rows=%.2f loops=%.0f)", rows, nloops);
    1852                 :        1018 :                 }
    1853                 :             :                 else
    1854                 :             :                 {
    1855         [ +  + ]:         163 :                         if (es->timing)
    1856                 :             :                         {
    1857                 :         294 :                                 ExplainPropertyFloat("Actual Startup Time", "ms", startup_ms,
    1858                 :         147 :                                                                          3, es);
    1859                 :         294 :                                 ExplainPropertyFloat("Actual Total Time", "ms", total_ms,
    1860                 :         147 :                                                                          3, es);
    1861                 :         147 :                         }
    1862                 :         163 :                         ExplainPropertyFloat("Actual Rows", NULL, rows, 2, es);
    1863                 :         163 :                         ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
    1864                 :             :                 }
    1865                 :        1181 :         }
    1866         [ +  + ]:       12191 :         else if (es->analyze)
    1867                 :             :         {
    1868         [ -  + ]:         123 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    1869                 :         123 :                         appendStringInfoString(es->str, " (never executed)");
    1870                 :             :                 else
    1871                 :             :                 {
    1872         [ #  # ]:           0 :                         if (es->timing)
    1873                 :             :                         {
    1874                 :           0 :                                 ExplainPropertyFloat("Actual Startup Time", "ms", 0.0, 3, es);
    1875                 :           0 :                                 ExplainPropertyFloat("Actual Total Time", "ms", 0.0, 3, es);
    1876                 :           0 :                         }
    1877                 :           0 :                         ExplainPropertyFloat("Actual Rows", NULL, 0.0, 0, es);
    1878                 :           0 :                         ExplainPropertyFloat("Actual Loops", NULL, 0.0, 0, es);
    1879                 :             :                 }
    1880                 :         123 :         }
    1881                 :             : 
    1882                 :             :         /* in text format, first line ends here */
    1883         [ +  + ]:       13372 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    1884                 :       13199 :                 appendStringInfoChar(es->str, '\n');
    1885                 :             : 
    1886                 :             : 
    1887                 :       13372 :         isdisabled = plan_is_disabled(plan);
    1888   [ +  +  +  + ]:       13372 :         if (es->format != EXPLAIN_FORMAT_TEXT || isdisabled)
    1889                 :         210 :                 ExplainPropertyBool("Disabled", isdisabled, es);
    1890                 :             : 
    1891                 :             :         /* prepare per-worker general execution details */
    1892   [ +  +  +  + ]:       13372 :         if (es->workers_state && es->verbose)
    1893                 :             :         {
    1894                 :           2 :                 WorkerInstrumentation *w = planstate->worker_instrument;
    1895                 :             : 
    1896         [ +  + ]:          10 :                 for (int n = 0; n < w->num_workers; n++)
    1897                 :             :                 {
    1898                 :           8 :                         Instrumentation *instrument = &w->instrument[n];
    1899                 :           8 :                         double          nloops = instrument->nloops;
    1900                 :           8 :                         double          startup_ms;
    1901                 :           8 :                         double          total_ms;
    1902                 :           8 :                         double          rows;
    1903                 :             : 
    1904         [ -  + ]:           8 :                         if (nloops <= 0)
    1905                 :           0 :                                 continue;
    1906                 :           8 :                         startup_ms = INSTR_TIME_GET_MILLISEC(instrument->startup) / nloops;
    1907                 :           8 :                         total_ms = INSTR_TIME_GET_MILLISEC(instrument->total) / nloops;
    1908                 :           8 :                         rows = instrument->ntuples / nloops;
    1909                 :             : 
    1910                 :           8 :                         ExplainOpenWorker(n, es);
    1911                 :             : 
    1912         [ +  - ]:           8 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    1913                 :             :                         {
    1914                 :           0 :                                 ExplainIndentText(es);
    1915                 :           0 :                                 appendStringInfoString(es->str, "actual ");
    1916         [ #  # ]:           0 :                                 if (es->timing)
    1917                 :           0 :                                         appendStringInfo(es->str, "time=%.3f..%.3f ", startup_ms, total_ms);
    1918                 :             : 
    1919                 :           0 :                                 appendStringInfo(es->str, "rows=%.2f loops=%.0f\n", rows, nloops);
    1920                 :           0 :                         }
    1921                 :             :                         else
    1922                 :             :                         {
    1923         [ -  + ]:           8 :                                 if (es->timing)
    1924                 :             :                                 {
    1925                 :           8 :                                         ExplainPropertyFloat("Actual Startup Time", "ms",
    1926                 :           8 :                                                                                  startup_ms, 3, es);
    1927                 :           8 :                                         ExplainPropertyFloat("Actual Total Time", "ms",
    1928                 :           8 :                                                                                  total_ms, 3, es);
    1929                 :           8 :                                 }
    1930                 :             : 
    1931                 :           8 :                                 ExplainPropertyFloat("Actual Rows", NULL, rows, 2, es);
    1932                 :           8 :                                 ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
    1933                 :             :                         }
    1934                 :             : 
    1935                 :           8 :                         ExplainCloseWorker(n, es);
    1936         [ -  + ]:           8 :                 }
    1937                 :           2 :         }
    1938                 :             : 
    1939                 :             :         /* target list */
    1940         [ +  + ]:       13372 :         if (es->verbose)
    1941                 :        1679 :                 show_plan_tlist(planstate, ancestors, es);
    1942                 :             : 
    1943                 :             :         /* unique join */
    1944         [ +  + ]:       13372 :         switch (nodeTag(plan))
    1945                 :             :         {
    1946                 :             :                 case T_NestLoop:
    1947                 :             :                 case T_MergeJoin:
    1948                 :             :                 case T_HashJoin:
    1949                 :             :                         /* try not to be too chatty about this in text mode */
    1950   [ +  +  +  + ]:        1607 :                         if (es->format != EXPLAIN_FORMAT_TEXT ||
    1951         [ +  + ]:        1326 :                                 (es->verbose && ((Join *) plan)->inner_unique))
    1952                 :          37 :                                 ExplainPropertyBool("Inner Unique",
    1953                 :          37 :                                                                         ((Join *) plan)->inner_unique,
    1954                 :          37 :                                                                         es);
    1955                 :        1349 :                         break;
    1956                 :             :                 default:
    1957                 :       12023 :                         break;
    1958                 :             :         }
    1959                 :             : 
    1960                 :             :         /* quals, sort keys, etc */
    1961   [ +  +  +  +  :       13372 :         switch (nodeTag(plan))
          +  +  +  +  +  
          +  +  +  +  +  
          -  -  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                      + ]
    1962                 :             :         {
    1963                 :             :                 case T_IndexScan:
    1964                 :        1070 :                         show_scan_qual(((IndexScan *) plan)->indexqualorig,
    1965                 :         535 :                                                    "Index Cond", planstate, ancestors, es);
    1966         [ +  + ]:         535 :                         if (((IndexScan *) plan)->indexqualorig)
    1967                 :         411 :                                 show_instrumentation_count("Rows Removed by Index Recheck", 2,
    1968                 :         411 :                                                                                    planstate, es);
    1969                 :        1070 :                         show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
    1970                 :         535 :                                                    "Order By", planstate, ancestors, es);
    1971                 :         535 :                         show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    1972         [ +  + ]:         535 :                         if (plan->qual)
    1973                 :          91 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    1974                 :          91 :                                                                                    planstate, es);
    1975                 :         535 :                         show_indexsearches_info(planstate, es);
    1976                 :         535 :                         break;
    1977                 :             :                 case T_IndexOnlyScan:
    1978                 :         808 :                         show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
    1979                 :         404 :                                                    "Index Cond", planstate, ancestors, es);
    1980         [ +  + ]:         404 :                         if (((IndexOnlyScan *) plan)->recheckqual)
    1981                 :         276 :                                 show_instrumentation_count("Rows Removed by Index Recheck", 2,
    1982                 :         276 :                                                                                    planstate, es);
    1983                 :         808 :                         show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
    1984                 :         404 :                                                    "Order By", planstate, ancestors, es);
    1985                 :         404 :                         show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    1986         [ +  + ]:         404 :                         if (plan->qual)
    1987                 :          22 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    1988                 :          22 :                                                                                    planstate, es);
    1989         [ +  + ]:         404 :                         if (es->analyze)
    1990                 :          16 :                                 ExplainPropertyFloat("Heap Fetches", NULL,
    1991                 :          16 :                                                                          planstate->instrument->ntuples2, 0, es);
    1992                 :         404 :                         show_indexsearches_info(planstate, es);
    1993                 :         404 :                         break;
    1994                 :             :                 case T_BitmapIndexScan:
    1995                 :        1410 :                         show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
    1996                 :         705 :                                                    "Index Cond", planstate, ancestors, es);
    1997                 :         705 :                         show_indexsearches_info(planstate, es);
    1998                 :         705 :                         break;
    1999                 :             :                 case T_BitmapHeapScan:
    2000                 :        1346 :                         show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
    2001                 :         673 :                                                    "Recheck Cond", planstate, ancestors, es);
    2002         [ +  + ]:         673 :                         if (((BitmapHeapScan *) plan)->bitmapqualorig)
    2003                 :         660 :                                 show_instrumentation_count("Rows Removed by Index Recheck", 2,
    2004                 :         660 :                                                                                    planstate, es);
    2005                 :         673 :                         show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2006         [ +  + ]:         673 :                         if (plan->qual)
    2007                 :          59 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2008                 :          59 :                                                                                    planstate, es);
    2009                 :         673 :                         show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
    2010                 :         673 :                         break;
    2011                 :             :                 case T_SampleScan:
    2012                 :          36 :                         show_tablesample(((SampleScan *) plan)->tablesample,
    2013                 :          18 :                                                          planstate, ancestors, es);
    2014                 :             :                         /* fall through to print additional fields the same as SeqScan */
    2015                 :             :                         /* FALLTHROUGH */
    2016                 :             :                 case T_SeqScan:
    2017                 :             :                 case T_ValuesScan:
    2018                 :             :                 case T_CteScan:
    2019                 :             :                 case T_NamedTuplestoreScan:
    2020                 :             :                 case T_WorkTableScan:
    2021                 :             :                 case T_SubqueryScan:
    2022                 :        4481 :                         show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2023         [ +  + ]:        4481 :                         if (plan->qual)
    2024                 :        2328 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2025                 :        2328 :                                                                                    planstate, es);
    2026         [ +  + ]:        4481 :                         if (IsA(plan, CteScan))
    2027                 :          41 :                                 show_ctescan_info(castNode(CteScanState, planstate), es);
    2028                 :        4481 :                         break;
    2029                 :             :                 case T_Gather:
    2030                 :             :                         {
    2031                 :         116 :                                 Gather     *gather = (Gather *) plan;
    2032                 :             : 
    2033                 :         116 :                                 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2034         [ +  - ]:         116 :                                 if (plan->qual)
    2035                 :           0 :                                         show_instrumentation_count("Rows Removed by Filter", 1,
    2036                 :           0 :                                                                                            planstate, es);
    2037                 :         116 :                                 ExplainPropertyInteger("Workers Planned", NULL,
    2038                 :         116 :                                                                            gather->num_workers, es);
    2039                 :             : 
    2040         [ +  + ]:         116 :                                 if (es->analyze)
    2041                 :             :                                 {
    2042                 :          28 :                                         int                     nworkers;
    2043                 :             : 
    2044                 :          28 :                                         nworkers = ((GatherState *) planstate)->nworkers_launched;
    2045                 :          28 :                                         ExplainPropertyInteger("Workers Launched", NULL,
    2046                 :          28 :                                                                                    nworkers, es);
    2047                 :          28 :                                 }
    2048                 :             : 
    2049   [ +  +  +  + ]:         116 :                                 if (gather->single_copy || es->format != EXPLAIN_FORMAT_TEXT)
    2050                 :          17 :                                         ExplainPropertyBool("Single Copy", gather->single_copy, es);
    2051                 :         116 :                         }
    2052                 :         116 :                         break;
    2053                 :             :                 case T_GatherMerge:
    2054                 :             :                         {
    2055                 :          46 :                                 GatherMerge *gm = (GatherMerge *) plan;
    2056                 :             : 
    2057                 :          46 :                                 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2058         [ -  + ]:          46 :                                 if (plan->qual)
    2059                 :           0 :                                         show_instrumentation_count("Rows Removed by Filter", 1,
    2060                 :           0 :                                                                                            planstate, es);
    2061                 :          46 :                                 ExplainPropertyInteger("Workers Planned", NULL,
    2062                 :          46 :                                                                            gm->num_workers, es);
    2063                 :             : 
    2064         [ +  + ]:          46 :                                 if (es->analyze)
    2065                 :             :                                 {
    2066                 :           2 :                                         int                     nworkers;
    2067                 :             : 
    2068                 :           2 :                                         nworkers = ((GatherMergeState *) planstate)->nworkers_launched;
    2069                 :           2 :                                         ExplainPropertyInteger("Workers Launched", NULL,
    2070                 :           2 :                                                                                    nworkers, es);
    2071                 :           2 :                                 }
    2072                 :          46 :                         }
    2073                 :          46 :                         break;
    2074                 :             :                 case T_FunctionScan:
    2075         [ +  + ]:         109 :                         if (es->verbose)
    2076                 :             :                         {
    2077                 :          33 :                                 List       *fexprs = NIL;
    2078                 :          33 :                                 ListCell   *lc;
    2079                 :             : 
    2080   [ +  -  +  +  :          66 :                                 foreach(lc, ((FunctionScan *) plan)->functions)
                   +  + ]
    2081                 :             :                                 {
    2082                 :          33 :                                         RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
    2083                 :             : 
    2084                 :          33 :                                         fexprs = lappend(fexprs, rtfunc->funcexpr);
    2085                 :          33 :                                 }
    2086                 :             :                                 /* We rely on show_expression to insert commas as needed */
    2087                 :          66 :                                 show_expression((Node *) fexprs,
    2088                 :          33 :                                                                 "Function Call", planstate, ancestors,
    2089                 :          33 :                                                                 es->verbose, es);
    2090                 :          33 :                         }
    2091                 :         109 :                         show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2092         [ +  + ]:         109 :                         if (plan->qual)
    2093                 :           5 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2094                 :           5 :                                                                                    planstate, es);
    2095                 :         109 :                         break;
    2096                 :             :                 case T_TableFuncScan:
    2097         [ +  + ]:          13 :                         if (es->verbose)
    2098                 :             :                         {
    2099                 :          12 :                                 TableFunc  *tablefunc = ((TableFuncScan *) plan)->tablefunc;
    2100                 :             : 
    2101                 :          24 :                                 show_expression((Node *) tablefunc,
    2102                 :          12 :                                                                 "Table Function Call", planstate, ancestors,
    2103                 :          12 :                                                                 es->verbose, es);
    2104                 :          12 :                         }
    2105                 :          13 :                         show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2106         [ +  + ]:          13 :                         if (plan->qual)
    2107                 :           3 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2108                 :           3 :                                                                                    planstate, es);
    2109                 :          26 :                         show_table_func_scan_info(castNode(TableFuncScanState,
    2110                 :          13 :                                                                                            planstate), es);
    2111                 :          13 :                         break;
    2112                 :             :                 case T_TidScan:
    2113                 :             :                         {
    2114                 :             :                                 /*
    2115                 :             :                                  * The tidquals list has OR semantics, so be sure to show it
    2116                 :             :                                  * as an OR condition.
    2117                 :             :                                  */
    2118                 :          13 :                                 List       *tidquals = ((TidScan *) plan)->tidquals;
    2119                 :             : 
    2120         [ +  + ]:          13 :                                 if (list_length(tidquals) > 1)
    2121                 :           2 :                                         tidquals = list_make1(make_orclause(tidquals));
    2122                 :          13 :                                 show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
    2123                 :          13 :                                 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2124         [ +  + ]:          13 :                                 if (plan->qual)
    2125                 :           3 :                                         show_instrumentation_count("Rows Removed by Filter", 1,
    2126                 :           3 :                                                                                            planstate, es);
    2127                 :          13 :                         }
    2128                 :          13 :                         break;
    2129                 :             :                 case T_TidRangeScan:
    2130                 :             :                         {
    2131                 :             :                                 /*
    2132                 :             :                                  * The tidrangequals list has AND semantics, so be sure to
    2133                 :             :                                  * show it as an AND condition.
    2134                 :             :                                  */
    2135                 :          21 :                                 List       *tidquals = ((TidRangeScan *) plan)->tidrangequals;
    2136                 :             : 
    2137         [ +  + ]:          21 :                                 if (list_length(tidquals) > 1)
    2138                 :           5 :                                         tidquals = list_make1(make_andclause(tidquals));
    2139                 :          21 :                                 show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
    2140                 :          21 :                                 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2141         [ +  - ]:          21 :                                 if (plan->qual)
    2142                 :           0 :                                         show_instrumentation_count("Rows Removed by Filter", 1,
    2143                 :           0 :                                                                                            planstate, es);
    2144                 :          21 :                         }
    2145                 :          21 :                         break;
    2146                 :             :                 case T_ForeignScan:
    2147                 :           0 :                         show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2148         [ #  # ]:           0 :                         if (plan->qual)
    2149                 :           0 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2150                 :           0 :                                                                                    planstate, es);
    2151                 :           0 :                         show_foreignscan_info((ForeignScanState *) planstate, es);
    2152                 :           0 :                         break;
    2153                 :             :                 case T_CustomScan:
    2154                 :             :                         {
    2155                 :           0 :                                 CustomScanState *css = (CustomScanState *) planstate;
    2156                 :             : 
    2157                 :           0 :                                 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
    2158         [ #  # ]:           0 :                                 if (plan->qual)
    2159                 :           0 :                                         show_instrumentation_count("Rows Removed by Filter", 1,
    2160                 :           0 :                                                                                            planstate, es);
    2161         [ #  # ]:           0 :                                 if (css->methods->ExplainCustomScan)
    2162                 :           0 :                                         css->methods->ExplainCustomScan(css, ancestors, es);
    2163                 :           0 :                         }
    2164                 :           0 :                         break;
    2165                 :             :                 case T_NestLoop:
    2166                 :        1236 :                         show_upper_qual(((NestLoop *) plan)->join.joinqual,
    2167                 :         618 :                                                         "Join Filter", planstate, ancestors, es);
    2168         [ +  + ]:         618 :                         if (((NestLoop *) plan)->join.joinqual)
    2169                 :         188 :                                 show_instrumentation_count("Rows Removed by Join Filter", 1,
    2170                 :         188 :                                                                                    planstate, es);
    2171                 :         618 :                         show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
    2172         [ +  + ]:         618 :                         if (plan->qual)
    2173                 :          15 :                                 show_instrumentation_count("Rows Removed by Filter", 2,
    2174                 :          15 :                                                                                    planstate, es);
    2175                 :         618 :                         break;
    2176                 :             :                 case T_MergeJoin:
    2177                 :         208 :                         show_upper_qual(((MergeJoin *) plan)->mergeclauses,
    2178                 :         104 :                                                         "Merge Cond", planstate, ancestors, es);
    2179                 :         208 :                         show_upper_qual(((MergeJoin *) plan)->join.joinqual,
    2180                 :         104 :                                                         "Join Filter", planstate, ancestors, es);
    2181         [ +  + ]:         104 :                         if (((MergeJoin *) plan)->join.joinqual)
    2182                 :           4 :                                 show_instrumentation_count("Rows Removed by Join Filter", 1,
    2183                 :           4 :                                                                                    planstate, es);
    2184                 :         104 :                         show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
    2185         [ +  + ]:         104 :                         if (plan->qual)
    2186                 :           4 :                                 show_instrumentation_count("Rows Removed by Filter", 2,
    2187                 :           4 :                                                                                    planstate, es);
    2188                 :         104 :                         break;
    2189                 :             :                 case T_HashJoin:
    2190                 :        1254 :                         show_upper_qual(((HashJoin *) plan)->hashclauses,
    2191                 :         627 :                                                         "Hash Cond", planstate, ancestors, es);
    2192                 :        1254 :                         show_upper_qual(((HashJoin *) plan)->join.joinqual,
    2193                 :         627 :                                                         "Join Filter", planstate, ancestors, es);
    2194         [ +  + ]:         627 :                         if (((HashJoin *) plan)->join.joinqual)
    2195                 :           4 :                                 show_instrumentation_count("Rows Removed by Join Filter", 1,
    2196                 :           4 :                                                                                    planstate, es);
    2197                 :         627 :                         show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
    2198         [ +  + ]:         627 :                         if (plan->qual)
    2199                 :          42 :                                 show_instrumentation_count("Rows Removed by Filter", 2,
    2200                 :          42 :                                                                                    planstate, es);
    2201                 :         627 :                         break;
    2202                 :             :                 case T_Agg:
    2203                 :        1715 :                         show_agg_keys(castNode(AggState, planstate), ancestors, es);
    2204                 :        1715 :                         show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
    2205                 :        1715 :                         show_hashagg_info((AggState *) planstate, es);
    2206         [ +  + ]:        1715 :                         if (plan->qual)
    2207                 :          58 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2208                 :          58 :                                                                                    planstate, es);
    2209                 :        1715 :                         break;
    2210                 :             :                 case T_WindowAgg:
    2211                 :          77 :                         show_window_def(castNode(WindowAggState, planstate), ancestors, es);
    2212                 :         154 :                         show_upper_qual(((WindowAgg *) plan)->runConditionOrig,
    2213                 :          77 :                                                         "Run Condition", planstate, ancestors, es);
    2214                 :          77 :                         show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
    2215         [ +  + ]:          77 :                         if (plan->qual)
    2216                 :           1 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2217                 :           1 :                                                                                    planstate, es);
    2218                 :          77 :                         show_windowagg_info(castNode(WindowAggState, planstate), es);
    2219                 :          77 :                         break;
    2220                 :             :                 case T_Group:
    2221                 :          16 :                         show_group_keys(castNode(GroupState, planstate), ancestors, es);
    2222                 :          16 :                         show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
    2223         [ +  - ]:          16 :                         if (plan->qual)
    2224                 :           0 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2225                 :           0 :                                                                                    planstate, es);
    2226                 :          16 :                         break;
    2227                 :             :                 case T_Sort:
    2228                 :         659 :                         show_sort_keys(castNode(SortState, planstate), ancestors, es);
    2229                 :         659 :                         show_sort_info(castNode(SortState, planstate), es);
    2230                 :         659 :                         break;
    2231                 :             :                 case T_IncrementalSort:
    2232                 :         128 :                         show_incremental_sort_keys(castNode(IncrementalSortState, planstate),
    2233                 :          64 :                                                                            ancestors, es);
    2234                 :         128 :                         show_incremental_sort_info(castNode(IncrementalSortState, planstate),
    2235                 :          64 :                                                                            es);
    2236                 :          64 :                         break;
    2237                 :             :                 case T_MergeAppend:
    2238                 :         112 :                         show_merge_append_keys(castNode(MergeAppendState, planstate),
    2239                 :          56 :                                                                    ancestors, es);
    2240                 :          56 :                         break;
    2241                 :             :                 case T_Result:
    2242                 :         426 :                         show_result_replacement_info(castNode(Result, plan), es);
    2243                 :         852 :                         show_upper_qual((List *) ((Result *) plan)->resconstantqual,
    2244                 :         426 :                                                         "One-Time Filter", planstate, ancestors, es);
    2245                 :         426 :                         show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
    2246         [ -  + ]:         426 :                         if (plan->qual)
    2247                 :           0 :                                 show_instrumentation_count("Rows Removed by Filter", 1,
    2248                 :           0 :                                                                                    planstate, es);
    2249                 :         426 :                         break;
    2250                 :             :                 case T_ModifyTable:
    2251                 :         284 :                         show_modifytable_info(castNode(ModifyTableState, planstate), ancestors,
    2252                 :         142 :                                                                   es);
    2253                 :         142 :                         break;
    2254                 :             :                 case T_Hash:
    2255                 :         627 :                         show_hash_info(castNode(HashState, planstate), es);
    2256                 :         627 :                         break;
    2257                 :             :                 case T_Material:
    2258                 :         179 :                         show_material_info(castNode(MaterialState, planstate), es);
    2259                 :         179 :                         break;
    2260                 :             :                 case T_Memoize:
    2261                 :          82 :                         show_memoize_info(castNode(MemoizeState, planstate), ancestors,
    2262                 :          41 :                                                           es);
    2263                 :          41 :                         break;
    2264                 :             :                 case T_RecursiveUnion:
    2265                 :          18 :                         show_recursive_union_info(castNode(RecursiveUnionState,
    2266                 :           9 :                                                                                            planstate), es);
    2267                 :           9 :                         break;
    2268                 :             :                 default:
    2269                 :         896 :                         break;
    2270                 :             :         }
    2271                 :             : 
    2272                 :             :         /*
    2273                 :             :          * Prepare per-worker JIT instrumentation.  As with the overall JIT
    2274                 :             :          * summary, this is printed only if printing costs is enabled.
    2275                 :             :          */
    2276   [ +  +  +  +  :       13372 :         if (es->workers_state && es->costs && es->verbose)
                   +  + ]
    2277                 :             :         {
    2278                 :           2 :                 SharedJitInstrumentation *w = planstate->worker_jit_instrument;
    2279                 :             : 
    2280         [ +  - ]:           2 :                 if (w)
    2281                 :             :                 {
    2282         [ #  # ]:           0 :                         for (int n = 0; n < w->num_workers; n++)
    2283                 :             :                         {
    2284                 :           0 :                                 ExplainOpenWorker(n, es);
    2285                 :           0 :                                 ExplainPrintJIT(es, planstate->state->es_jit_flags,
    2286                 :           0 :                                                                 &w->jit_instr[n]);
    2287                 :           0 :                                 ExplainCloseWorker(n, es);
    2288                 :           0 :                         }
    2289                 :           0 :                 }
    2290                 :           2 :         }
    2291                 :             : 
    2292                 :             :         /* Show buffer/WAL usage */
    2293   [ +  +  -  + ]:       13372 :         if (es->buffers && planstate->instrument)
    2294                 :         695 :                 show_buffer_usage(es, &planstate->instrument->bufusage);
    2295   [ -  +  #  # ]:       13372 :         if (es->wal && planstate->instrument)
    2296                 :           0 :                 show_wal_usage(es, &planstate->instrument->walusage);
    2297                 :             : 
    2298                 :             :         /* Prepare per-worker buffer/WAL usage */
    2299   [ +  +  +  +  :       13372 :         if (es->workers_state && (es->buffers || es->wal) && es->verbose)
                   +  + ]
    2300                 :             :         {
    2301                 :           2 :                 WorkerInstrumentation *w = planstate->worker_instrument;
    2302                 :             : 
    2303         [ +  + ]:          10 :                 for (int n = 0; n < w->num_workers; n++)
    2304                 :             :                 {
    2305                 :           8 :                         Instrumentation *instrument = &w->instrument[n];
    2306                 :           8 :                         double          nloops = instrument->nloops;
    2307                 :             : 
    2308         [ -  + ]:           8 :                         if (nloops <= 0)
    2309                 :           0 :                                 continue;
    2310                 :             : 
    2311                 :           8 :                         ExplainOpenWorker(n, es);
    2312         [ -  + ]:           8 :                         if (es->buffers)
    2313                 :           8 :                                 show_buffer_usage(es, &instrument->bufusage);
    2314         [ +  - ]:           8 :                         if (es->wal)
    2315                 :           0 :                                 show_wal_usage(es, &instrument->walusage);
    2316                 :           8 :                         ExplainCloseWorker(n, es);
    2317         [ -  + ]:           8 :                 }
    2318                 :           2 :         }
    2319                 :             : 
    2320                 :             :         /* Show per-worker details for this plan node, then pop that stack */
    2321         [ +  + ]:       13372 :         if (es->workers_state)
    2322                 :         171 :                 ExplainFlushWorkersState(es);
    2323                 :       13372 :         es->workers_state = save_workers_state;
    2324                 :             : 
    2325                 :             :         /* Allow plugins to print additional information */
    2326         [ +  - ]:       13372 :         if (explain_per_node_hook)
    2327                 :           0 :                 (*explain_per_node_hook) (planstate, ancestors, relationship,
    2328                 :           0 :                                                                   plan_name, es);
    2329                 :             : 
    2330                 :             :         /*
    2331                 :             :          * If partition pruning was done during executor initialization, the
    2332                 :             :          * number of child plans we'll display below will be less than the number
    2333                 :             :          * of subplans that was specified in the plan.  To make this a bit less
    2334                 :             :          * mysterious, emit an indication that this happened.  Note that this
    2335                 :             :          * field is emitted now because we want it to be a property of the parent
    2336                 :             :          * node; it *cannot* be emitted within the Plans sub-node we'll open next.
    2337                 :             :          */
    2338      [ +  +  + ]:       13372 :         switch (nodeTag(plan))
    2339                 :             :         {
    2340                 :             :                 case T_Append:
    2341                 :        1154 :                         ExplainMissingMembers(((AppendState *) planstate)->as_nplans,
    2342                 :         577 :                                                                   list_length(((Append *) plan)->appendplans),
    2343                 :         577 :                                                                   es);
    2344                 :         577 :                         break;
    2345                 :             :                 case T_MergeAppend:
    2346                 :         112 :                         ExplainMissingMembers(((MergeAppendState *) planstate)->ms_nplans,
    2347                 :          56 :                                                                   list_length(((MergeAppend *) plan)->mergeplans),
    2348                 :          56 :                                                                   es);
    2349                 :          56 :                         break;
    2350                 :             :                 default:
    2351                 :       12739 :                         break;
    2352                 :             :         }
    2353                 :             : 
    2354                 :             :         /* Get ready to display the child plans */
    2355         [ +  + ]:       26623 :         haschildren = planstate->initPlan ||
    2356         [ +  + ]:       13251 :                 outerPlanState(planstate) ||
    2357         [ +  - ]:        7267 :                 innerPlanState(planstate) ||
    2358         [ +  + ]:        7267 :                 IsA(plan, Append) ||
    2359         [ +  + ]:        6709 :                 IsA(plan, MergeAppend) ||
    2360         [ +  + ]:        6654 :                 IsA(plan, BitmapAnd) ||
    2361         [ +  + ]:        6647 :                 IsA(plan, BitmapOr) ||
    2362         [ +  + ]:        6623 :                 IsA(plan, SubqueryScan) ||
    2363         [ -  + ]:        6550 :                 (IsA(planstate, CustomScanState) &&
    2364         [ #  # ]:        6550 :                  ((CustomScanState *) planstate)->custom_ps != NIL) ||
    2365                 :        6550 :                 planstate->subPlan;
    2366         [ +  + ]:       13372 :         if (haschildren)
    2367                 :             :         {
    2368                 :        6883 :                 ExplainOpenGroup("Plans", "Plans", false, es);
    2369                 :             :                 /* Pass current Plan as head of ancestors list for children */
    2370                 :        6883 :                 ancestors = lcons(plan, ancestors);
    2371                 :        6883 :         }
    2372                 :             : 
    2373                 :             :         /* initPlan-s */
    2374         [ +  + ]:       13372 :         if (planstate->initPlan)
    2375                 :         121 :                 ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
    2376                 :             : 
    2377                 :             :         /* lefttree */
    2378         [ +  + ]:       13372 :         if (outerPlanState(planstate))
    2379                 :       12076 :                 ExplainNode(outerPlanState(planstate), ancestors,
    2380                 :        6038 :                                         "Outer", NULL, es);
    2381                 :             : 
    2382                 :             :         /* righttree */
    2383         [ +  + ]:       13372 :         if (innerPlanState(planstate))
    2384                 :        2754 :                 ExplainNode(innerPlanState(planstate), ancestors,
    2385                 :        1377 :                                         "Inner", NULL, es);
    2386                 :             : 
    2387                 :             :         /* special child plans */
    2388   [ +  +  +  +  :       13372 :         switch (nodeTag(plan))
                +  +  - ]
    2389                 :             :         {
    2390                 :             :                 case T_Append:
    2391                 :        1154 :                         ExplainMemberNodes(((AppendState *) planstate)->appendplans,
    2392                 :         577 :                                                            ((AppendState *) planstate)->as_nplans,
    2393                 :         577 :                                                            ancestors, es);
    2394                 :         577 :                         break;
    2395                 :             :                 case T_MergeAppend:
    2396                 :         112 :                         ExplainMemberNodes(((MergeAppendState *) planstate)->mergeplans,
    2397                 :          56 :                                                            ((MergeAppendState *) planstate)->ms_nplans,
    2398                 :          56 :                                                            ancestors, es);
    2399                 :          56 :                         break;
    2400                 :             :                 case T_BitmapAnd:
    2401                 :          14 :                         ExplainMemberNodes(((BitmapAndState *) planstate)->bitmapplans,
    2402                 :           7 :                                                            ((BitmapAndState *) planstate)->nplans,
    2403                 :           7 :                                                            ancestors, es);
    2404                 :           7 :                         break;
    2405                 :             :                 case T_BitmapOr:
    2406                 :          48 :                         ExplainMemberNodes(((BitmapOrState *) planstate)->bitmapplans,
    2407                 :          24 :                                                            ((BitmapOrState *) planstate)->nplans,
    2408                 :          24 :                                                            ancestors, es);
    2409                 :          24 :                         break;
    2410                 :             :                 case T_SubqueryScan:
    2411                 :         146 :                         ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
    2412                 :          73 :                                                 "Subquery", NULL, es);
    2413                 :          73 :                         break;
    2414                 :             :                 case T_CustomScan:
    2415                 :           0 :                         ExplainCustomChildren((CustomScanState *) planstate,
    2416                 :           0 :                                                                   ancestors, es);
    2417                 :           0 :                         break;
    2418                 :             :                 default:
    2419                 :       12635 :                         break;
    2420                 :             :         }
    2421                 :             : 
    2422                 :             :         /* subPlan-s */
    2423         [ +  + ]:       13372 :         if (planstate->subPlan)
    2424                 :          99 :                 ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
    2425                 :             : 
    2426                 :             :         /* end of child plans */
    2427         [ +  + ]:       13372 :         if (haschildren)
    2428                 :             :         {
    2429                 :        6883 :                 ancestors = list_delete_first(ancestors);
    2430                 :        6883 :                 ExplainCloseGroup("Plans", "Plans", false, es);
    2431                 :        6883 :         }
    2432                 :             : 
    2433                 :             :         /* in text format, undo whatever indentation we added */
    2434         [ +  + ]:       13372 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    2435                 :       13199 :                 es->indent = save_indent;
    2436                 :             : 
    2437                 :       13372 :         ExplainCloseGroup("Plan",
    2438                 :       13372 :                                           relationship ? NULL : "Plan",
    2439                 :       13372 :                                           true, es);
    2440                 :       13372 : }
    2441                 :             : 
    2442                 :             : /*
    2443                 :             :  * Show the targetlist of a plan node
    2444                 :             :  */
    2445                 :             : static void
    2446                 :        1679 : show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
    2447                 :             : {
    2448                 :        1679 :         Plan       *plan = planstate->plan;
    2449                 :        1679 :         List       *context;
    2450                 :        1679 :         List       *result = NIL;
    2451                 :        1679 :         bool            useprefix;
    2452                 :        1679 :         ListCell   *lc;
    2453                 :             : 
    2454                 :             :         /* No work if empty tlist (this occurs eg in bitmap indexscans) */
    2455         [ +  + ]:        1679 :         if (plan->targetlist == NIL)
    2456                 :          61 :                 return;
    2457                 :             :         /* The tlist of an Append isn't real helpful, so suppress it */
    2458         [ +  + ]:        1618 :         if (IsA(plan, Append))
    2459                 :          34 :                 return;
    2460                 :             :         /* Likewise for MergeAppend and RecursiveUnion */
    2461         [ +  + ]:        1584 :         if (IsA(plan, MergeAppend))
    2462                 :           5 :                 return;
    2463         [ +  + ]:        1579 :         if (IsA(plan, RecursiveUnion))
    2464                 :           8 :                 return;
    2465                 :             : 
    2466                 :             :         /*
    2467                 :             :          * Likewise for ForeignScan that executes a direct INSERT/UPDATE/DELETE
    2468                 :             :          *
    2469                 :             :          * Note: the tlist for a ForeignScan that executes a direct INSERT/UPDATE
    2470                 :             :          * might contain subplan output expressions that are confusing in this
    2471                 :             :          * context.  The tlist for a ForeignScan that executes a direct UPDATE/
    2472                 :             :          * DELETE always contains "junk" target columns to identify the exact row
    2473                 :             :          * to update or delete, which would be confusing in this context.  So, we
    2474                 :             :          * suppress it in all the cases.
    2475                 :             :          */
    2476   [ -  +  #  # ]:        1571 :         if (IsA(plan, ForeignScan) &&
    2477                 :           0 :                 ((ForeignScan *) plan)->operation != CMD_SELECT)
    2478                 :           0 :                 return;
    2479                 :             : 
    2480                 :             :         /* Set up deparsing context */
    2481                 :        3142 :         context = set_deparse_context_plan(es->deparse_cxt,
    2482                 :        1571 :                                                                            plan,
    2483                 :        1571 :                                                                            ancestors);
    2484                 :        1571 :         useprefix = es->rtable_size > 1;
    2485                 :             : 
    2486                 :             :         /* Deparse each result column (we now include resjunk ones) */
    2487   [ +  -  +  +  :        5277 :         foreach(lc, plan->targetlist)
                   +  + ]
    2488                 :             :         {
    2489                 :        3706 :                 TargetEntry *tle = (TargetEntry *) lfirst(lc);
    2490                 :             : 
    2491                 :        7412 :                 result = lappend(result,
    2492                 :        7412 :                                                  deparse_expression((Node *) tle->expr, context,
    2493                 :        3706 :                                                                                         useprefix, false));
    2494                 :        3706 :         }
    2495                 :             : 
    2496                 :             :         /* Print results */
    2497                 :        1571 :         ExplainPropertyList("Output", result, es);
    2498         [ -  + ]:        1679 : }
    2499                 :             : 
    2500                 :             : /*
    2501                 :             :  * Show a generic expression
    2502                 :             :  */
    2503                 :             : static void
    2504                 :        5861 : show_expression(Node *node, const char *qlabel,
    2505                 :             :                                 PlanState *planstate, List *ancestors,
    2506                 :             :                                 bool useprefix, ExplainState *es)
    2507                 :             : {
    2508                 :        5861 :         List       *context;
    2509                 :        5861 :         char       *exprstr;
    2510                 :             : 
    2511                 :             :         /* Set up deparsing context */
    2512                 :       11722 :         context = set_deparse_context_plan(es->deparse_cxt,
    2513                 :        5861 :                                                                            planstate->plan,
    2514                 :        5861 :                                                                            ancestors);
    2515                 :             : 
    2516                 :             :         /* Deparse the expression */
    2517                 :        5861 :         exprstr = deparse_expression(node, context, useprefix, false);
    2518                 :             : 
    2519                 :             :         /* And add to es->str */
    2520                 :        5861 :         ExplainPropertyText(qlabel, exprstr, es);
    2521                 :        5861 : }
    2522                 :             : 
    2523                 :             : /*
    2524                 :             :  * Show a qualifier expression (which is a List with implicit AND semantics)
    2525                 :             :  */
    2526                 :             : static void
    2527                 :       15876 : show_qual(List *qual, const char *qlabel,
    2528                 :             :                   PlanState *planstate, List *ancestors,
    2529                 :             :                   bool useprefix, ExplainState *es)
    2530                 :             : {
    2531                 :       15876 :         Node       *node;
    2532                 :             : 
    2533                 :             :         /* No work if empty qual */
    2534         [ +  + ]:       15876 :         if (qual == NIL)
    2535                 :       10060 :                 return;
    2536                 :             : 
    2537                 :             :         /* Convert AND list to explicit AND */
    2538                 :        5816 :         node = (Node *) make_ands_explicit(qual);
    2539                 :             : 
    2540                 :             :         /* And show it */
    2541                 :        5816 :         show_expression(node, qlabel, planstate, ancestors, useprefix, es);
    2542         [ -  + ]:       15876 : }
    2543                 :             : 
    2544                 :             : /*
    2545                 :             :  * Show a qualifier expression for a scan plan node
    2546                 :             :  */
    2547                 :             : static void
    2548                 :        9701 : show_scan_qual(List *qual, const char *qlabel,
    2549                 :             :                            PlanState *planstate, List *ancestors,
    2550                 :             :                            ExplainState *es)
    2551                 :             : {
    2552                 :        9701 :         bool            useprefix;
    2553                 :             : 
    2554         [ +  + ]:        9701 :         useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
    2555                 :        9701 :         show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
    2556                 :        9701 : }
    2557                 :             : 
    2558                 :             : /*
    2559                 :             :  * Show a qualifier expression for an upper-level plan node
    2560                 :             :  */
    2561                 :             : static void
    2562                 :        6175 : show_upper_qual(List *qual, const char *qlabel,
    2563                 :             :                                 PlanState *planstate, List *ancestors,
    2564                 :             :                                 ExplainState *es)
    2565                 :             : {
    2566                 :        6175 :         bool            useprefix;
    2567                 :             : 
    2568         [ +  + ]:        6175 :         useprefix = (es->rtable_size > 1 || es->verbose);
    2569                 :        6175 :         show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
    2570                 :        6175 : }
    2571                 :             : 
    2572                 :             : /*
    2573                 :             :  * Show the sort keys for a Sort node.
    2574                 :             :  */
    2575                 :             : static void
    2576                 :         659 : show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
    2577                 :             : {
    2578                 :         659 :         Sort       *plan = (Sort *) sortstate->ss.ps.plan;
    2579                 :             : 
    2580                 :        1318 :         show_sort_group_keys((PlanState *) sortstate, "Sort Key",
    2581                 :         659 :                                                  plan->numCols, 0, plan->sortColIdx,
    2582                 :         659 :                                                  plan->sortOperators, plan->collations,
    2583                 :         659 :                                                  plan->nullsFirst,
    2584                 :         659 :                                                  ancestors, es);
    2585                 :         659 : }
    2586                 :             : 
    2587                 :             : /*
    2588                 :             :  * Show the sort keys for an IncrementalSort node.
    2589                 :             :  */
    2590                 :             : static void
    2591                 :          64 : show_incremental_sort_keys(IncrementalSortState *incrsortstate,
    2592                 :             :                                                    List *ancestors, ExplainState *es)
    2593                 :             : {
    2594                 :          64 :         IncrementalSort *plan = (IncrementalSort *) incrsortstate->ss.ps.plan;
    2595                 :             : 
    2596                 :         128 :         show_sort_group_keys((PlanState *) incrsortstate, "Sort Key",
    2597                 :          64 :                                                  plan->sort.numCols, plan->nPresortedCols,
    2598                 :          64 :                                                  plan->sort.sortColIdx,
    2599                 :          64 :                                                  plan->sort.sortOperators, plan->sort.collations,
    2600                 :          64 :                                                  plan->sort.nullsFirst,
    2601                 :          64 :                                                  ancestors, es);
    2602                 :          64 : }
    2603                 :             : 
    2604                 :             : /*
    2605                 :             :  * Likewise, for a MergeAppend node.
    2606                 :             :  */
    2607                 :             : static void
    2608                 :          56 : show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
    2609                 :             :                                            ExplainState *es)
    2610                 :             : {
    2611                 :          56 :         MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
    2612                 :             : 
    2613                 :         112 :         show_sort_group_keys((PlanState *) mstate, "Sort Key",
    2614                 :          56 :                                                  plan->numCols, 0, plan->sortColIdx,
    2615                 :          56 :                                                  plan->sortOperators, plan->collations,
    2616                 :          56 :                                                  plan->nullsFirst,
    2617                 :          56 :                                                  ancestors, es);
    2618                 :          56 : }
    2619                 :             : 
    2620                 :             : /*
    2621                 :             :  * Show the grouping keys for an Agg node.
    2622                 :             :  */
    2623                 :             : static void
    2624                 :        1715 : show_agg_keys(AggState *astate, List *ancestors,
    2625                 :             :                           ExplainState *es)
    2626                 :             : {
    2627                 :        1715 :         Agg                *plan = (Agg *) astate->ss.ps.plan;
    2628                 :             : 
    2629   [ +  +  +  + ]:        1715 :         if (plan->numCols > 0 || plan->groupingSets)
    2630                 :             :         {
    2631                 :             :                 /* The key columns refer to the tlist of the child plan */
    2632                 :         498 :                 ancestors = lcons(plan, ancestors);
    2633                 :             : 
    2634         [ +  + ]:         498 :                 if (plan->groupingSets)
    2635                 :          49 :                         show_grouping_sets(outerPlanState(astate), plan, ancestors, es);
    2636                 :             :                 else
    2637                 :         898 :                         show_sort_group_keys(outerPlanState(astate), "Group Key",
    2638                 :         449 :                                                                  plan->numCols, 0, plan->grpColIdx,
    2639                 :             :                                                                  NULL, NULL, NULL,
    2640                 :         449 :                                                                  ancestors, es);
    2641                 :             : 
    2642                 :         498 :                 ancestors = list_delete_first(ancestors);
    2643                 :         498 :         }
    2644                 :        1715 : }
    2645                 :             : 
    2646                 :             : static void
    2647                 :          49 : show_grouping_sets(PlanState *planstate, Agg *agg,
    2648                 :             :                                    List *ancestors, ExplainState *es)
    2649                 :             : {
    2650                 :          49 :         List       *context;
    2651                 :          49 :         bool            useprefix;
    2652                 :          49 :         ListCell   *lc;
    2653                 :             : 
    2654                 :             :         /* Set up deparsing context */
    2655                 :          98 :         context = set_deparse_context_plan(es->deparse_cxt,
    2656                 :          49 :                                                                            planstate->plan,
    2657                 :          49 :                                                                            ancestors);
    2658         [ +  + ]:          49 :         useprefix = (es->rtable_size > 1 || es->verbose);
    2659                 :             : 
    2660                 :          49 :         ExplainOpenGroup("Grouping Sets", "Grouping Sets", false, es);
    2661                 :             : 
    2662                 :          98 :         show_grouping_set_keys(planstate, agg, NULL,
    2663                 :          49 :                                                    context, useprefix, ancestors, es);
    2664                 :             : 
    2665   [ +  +  +  +  :         120 :         foreach(lc, agg->chain)
                   +  + ]
    2666                 :             :         {
    2667                 :          71 :                 Agg                *aggnode = lfirst(lc);
    2668                 :          71 :                 Sort       *sortnode = (Sort *) aggnode->plan.lefttree;
    2669                 :             : 
    2670                 :         142 :                 show_grouping_set_keys(planstate, aggnode, sortnode,
    2671                 :          71 :                                                            context, useprefix, ancestors, es);
    2672                 :          71 :         }
    2673                 :             : 
    2674                 :          49 :         ExplainCloseGroup("Grouping Sets", "Grouping Sets", false, es);
    2675                 :          49 : }
    2676                 :             : 
    2677                 :             : static void
    2678                 :         120 : show_grouping_set_keys(PlanState *planstate,
    2679                 :             :                                            Agg *aggnode, Sort *sortnode,
    2680                 :             :                                            List *context, bool useprefix,
    2681                 :             :                                            List *ancestors, ExplainState *es)
    2682                 :             : {
    2683                 :         120 :         Plan       *plan = planstate->plan;
    2684                 :         120 :         char       *exprstr;
    2685                 :         120 :         ListCell   *lc;
    2686                 :         120 :         List       *gsets = aggnode->groupingSets;
    2687                 :         120 :         AttrNumber *keycols = aggnode->grpColIdx;
    2688                 :         120 :         const char *keyname;
    2689                 :         120 :         const char *keysetname;
    2690                 :             : 
    2691   [ +  +  +  + ]:         120 :         if (aggnode->aggstrategy == AGG_HASHED || aggnode->aggstrategy == AGG_MIXED)
    2692                 :             :         {
    2693                 :          70 :                 keyname = "Hash Key";
    2694                 :          70 :                 keysetname = "Hash Keys";
    2695                 :          70 :         }
    2696                 :             :         else
    2697                 :             :         {
    2698                 :          50 :                 keyname = "Group Key";
    2699                 :          50 :                 keysetname = "Group Keys";
    2700                 :             :         }
    2701                 :             : 
    2702                 :         120 :         ExplainOpenGroup("Grouping Set", NULL, true, es);
    2703                 :             : 
    2704         [ +  + ]:         120 :         if (sortnode)
    2705                 :             :         {
    2706                 :          26 :                 show_sort_group_keys(planstate, "Sort Key",
    2707                 :          13 :                                                          sortnode->numCols, 0, sortnode->sortColIdx,
    2708                 :          13 :                                                          sortnode->sortOperators, sortnode->collations,
    2709                 :          13 :                                                          sortnode->nullsFirst,
    2710                 :          13 :                                                          ancestors, es);
    2711         [ -  + ]:          13 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    2712                 :          13 :                         es->indent++;
    2713                 :          13 :         }
    2714                 :             : 
    2715                 :         120 :         ExplainOpenGroup(keysetname, keysetname, false, es);
    2716                 :             : 
    2717   [ +  -  +  +  :         259 :         foreach(lc, gsets)
                   +  + ]
    2718                 :             :         {
    2719                 :         139 :                 List       *result = NIL;
    2720                 :         139 :                 ListCell   *lc2;
    2721                 :             : 
    2722   [ +  +  +  +  :         284 :                 foreach(lc2, (List *) lfirst(lc))
                   +  + ]
    2723                 :             :                 {
    2724                 :         145 :                         Index           i = lfirst_int(lc2);
    2725                 :         145 :                         AttrNumber      keyresno = keycols[i];
    2726                 :         290 :                         TargetEntry *target = get_tle_by_resno(plan->targetlist,
    2727                 :         145 :                                                                                                    keyresno);
    2728                 :             : 
    2729         [ +  - ]:         145 :                         if (!target)
    2730   [ #  #  #  # ]:           0 :                                 elog(ERROR, "no tlist entry for key %d", keyresno);
    2731                 :             :                         /* Deparse the expression, showing any top-level cast */
    2732                 :         290 :                         exprstr = deparse_expression((Node *) target->expr, context,
    2733                 :         145 :                                                                                  useprefix, true);
    2734                 :             : 
    2735                 :         145 :                         result = lappend(result, exprstr);
    2736                 :         145 :                 }
    2737                 :             : 
    2738   [ +  +  +  - ]:         139 :                 if (!result && es->format == EXPLAIN_FORMAT_TEXT)
    2739                 :          27 :                         ExplainPropertyText(keyname, "()", es);
    2740                 :             :                 else
    2741                 :         112 :                         ExplainPropertyListNested(keyname, result, es);
    2742                 :         139 :         }
    2743                 :             : 
    2744                 :         120 :         ExplainCloseGroup(keysetname, keysetname, false, es);
    2745                 :             : 
    2746   [ +  +  -  + ]:         120 :         if (sortnode && es->format == EXPLAIN_FORMAT_TEXT)
    2747                 :          13 :                 es->indent--;
    2748                 :             : 
    2749                 :         120 :         ExplainCloseGroup("Grouping Set", NULL, true, es);
    2750                 :         120 : }
    2751                 :             : 
    2752                 :             : /*
    2753                 :             :  * Show the grouping keys for a Group node.
    2754                 :             :  */
    2755                 :             : static void
    2756                 :          16 : show_group_keys(GroupState *gstate, List *ancestors,
    2757                 :             :                                 ExplainState *es)
    2758                 :             : {
    2759                 :          16 :         Group      *plan = (Group *) gstate->ss.ps.plan;
    2760                 :             : 
    2761                 :             :         /* The key columns refer to the tlist of the child plan */
    2762                 :          16 :         ancestors = lcons(plan, ancestors);
    2763                 :          32 :         show_sort_group_keys(outerPlanState(gstate), "Group Key",
    2764                 :          16 :                                                  plan->numCols, 0, plan->grpColIdx,
    2765                 :             :                                                  NULL, NULL, NULL,
    2766                 :          16 :                                                  ancestors, es);
    2767                 :          16 :         ancestors = list_delete_first(ancestors);
    2768                 :          16 : }
    2769                 :             : 
    2770                 :             : /*
    2771                 :             :  * Common code to show sort/group keys, which are represented in plan nodes
    2772                 :             :  * as arrays of targetlist indexes.  If it's a sort key rather than a group
    2773                 :             :  * key, also pass sort operators/collations/nullsFirst arrays.
    2774                 :             :  */
    2775                 :             : static void
    2776                 :        1257 : show_sort_group_keys(PlanState *planstate, const char *qlabel,
    2777                 :             :                                          int nkeys, int nPresortedKeys, AttrNumber *keycols,
    2778                 :             :                                          Oid *sortOperators, Oid *collations, bool *nullsFirst,
    2779                 :             :                                          List *ancestors, ExplainState *es)
    2780                 :             : {
    2781                 :        1257 :         Plan       *plan = planstate->plan;
    2782                 :        1257 :         List       *context;
    2783                 :        1257 :         List       *result = NIL;
    2784                 :        1257 :         List       *resultPresorted = NIL;
    2785                 :        1257 :         StringInfoData sortkeybuf;
    2786                 :        1257 :         bool            useprefix;
    2787                 :        1257 :         int                     keyno;
    2788                 :             : 
    2789         [ -  + ]:        1257 :         if (nkeys <= 0)
    2790                 :           0 :                 return;
    2791                 :             : 
    2792                 :        1257 :         initStringInfo(&sortkeybuf);
    2793                 :             : 
    2794                 :             :         /* Set up deparsing context */
    2795                 :        2514 :         context = set_deparse_context_plan(es->deparse_cxt,
    2796                 :        1257 :                                                                            plan,
    2797                 :        1257 :                                                                            ancestors);
    2798         [ +  + ]:        1257 :         useprefix = (es->rtable_size > 1 || es->verbose);
    2799                 :             : 
    2800         [ +  + ]:        3181 :         for (keyno = 0; keyno < nkeys; keyno++)
    2801                 :             :         {
    2802                 :             :                 /* find key expression in tlist */
    2803                 :        1924 :                 AttrNumber      keyresno = keycols[keyno];
    2804                 :        3848 :                 TargetEntry *target = get_tle_by_resno(plan->targetlist,
    2805                 :        1924 :                                                                                            keyresno);
    2806                 :        1924 :                 char       *exprstr;
    2807                 :             : 
    2808         [ +  - ]:        1924 :                 if (!target)
    2809   [ #  #  #  # ]:           0 :                         elog(ERROR, "no tlist entry for key %d", keyresno);
    2810                 :             :                 /* Deparse the expression, showing any top-level cast */
    2811                 :        3848 :                 exprstr = deparse_expression((Node *) target->expr, context,
    2812                 :        1924 :                                                                          useprefix, true);
    2813                 :        1924 :                 resetStringInfo(&sortkeybuf);
    2814                 :        1924 :                 appendStringInfoString(&sortkeybuf, exprstr);
    2815                 :             :                 /* Append sort order information, if relevant */
    2816         [ +  + ]:        1924 :                 if (sortOperators != NULL)
    2817                 :        1188 :                         show_sortorder_options(&sortkeybuf,
    2818                 :        1188 :                                                                    (Node *) target->expr,
    2819                 :        1188 :                                                                    sortOperators[keyno],
    2820                 :        1188 :                                                                    collations[keyno],
    2821                 :        1188 :                                                                    nullsFirst[keyno]);
    2822                 :             :                 /* Emit one property-list item per sort key */
    2823                 :        1924 :                 result = lappend(result, pstrdup(sortkeybuf.data));
    2824         [ +  + ]:        1924 :                 if (keyno < nPresortedKeys)
    2825                 :          70 :                         resultPresorted = lappend(resultPresorted, exprstr);
    2826                 :        1924 :         }
    2827                 :             : 
    2828                 :        1257 :         ExplainPropertyList(qlabel, result, es);
    2829         [ +  + ]:        1257 :         if (nPresortedKeys > 0)
    2830                 :          64 :                 ExplainPropertyList("Presorted Key", resultPresorted, es);
    2831         [ -  + ]:        1257 : }
    2832                 :             : 
    2833                 :             : /*
    2834                 :             :  * Append nondefault characteristics of the sort ordering of a column to buf
    2835                 :             :  * (collation, direction, NULLS FIRST/LAST)
    2836                 :             :  */
    2837                 :             : static void
    2838                 :        1188 : show_sortorder_options(StringInfo buf, Node *sortexpr,
    2839                 :             :                                            Oid sortOperator, Oid collation, bool nullsFirst)
    2840                 :             : {
    2841                 :        1188 :         Oid                     sortcoltype = exprType(sortexpr);
    2842                 :        1188 :         bool            reverse = false;
    2843                 :        1188 :         TypeCacheEntry *typentry;
    2844                 :             : 
    2845                 :        1188 :         typentry = lookup_type_cache(sortcoltype,
    2846                 :             :                                                                  TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
    2847                 :             : 
    2848                 :             :         /*
    2849                 :             :          * Print COLLATE if it's not default for the column's type.  There are
    2850                 :             :          * some cases where this is redundant, eg if expression is a column whose
    2851                 :             :          * declared collation is that collation, but it's hard to distinguish that
    2852                 :             :          * here (and arguably, printing COLLATE explicitly is a good idea anyway
    2853                 :             :          * in such cases).
    2854                 :             :          */
    2855   [ +  +  +  + ]:        1188 :         if (OidIsValid(collation) && collation != get_typcollation(sortcoltype))
    2856                 :             :         {
    2857                 :          18 :                 char       *collname = get_collation_name(collation);
    2858                 :             : 
    2859         [ +  - ]:          18 :                 if (collname == NULL)
    2860   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for collation %u", collation);
    2861                 :          18 :                 appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
    2862                 :          18 :         }
    2863                 :             : 
    2864                 :             :         /* Print direction if not ASC, or USING if non-default sort operator */
    2865         [ +  + ]:        1188 :         if (sortOperator == typentry->gt_opr)
    2866                 :             :         {
    2867                 :          41 :                 appendStringInfoString(buf, " DESC");
    2868                 :          41 :                 reverse = true;
    2869                 :          41 :         }
    2870         [ +  + ]:        1147 :         else if (sortOperator != typentry->lt_opr)
    2871                 :             :         {
    2872                 :           3 :                 char       *opname = get_opname(sortOperator);
    2873                 :             : 
    2874         [ +  - ]:           3 :                 if (opname == NULL)
    2875   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for operator %u", sortOperator);
    2876                 :           3 :                 appendStringInfo(buf, " USING %s", opname);
    2877                 :             :                 /* Determine whether operator would be considered ASC or DESC */
    2878                 :           3 :                 (void) get_equality_op_for_ordering_op(sortOperator, &reverse);
    2879                 :           3 :         }
    2880                 :             : 
    2881                 :             :         /* Add NULLS FIRST/LAST only if it wouldn't be default */
    2882   [ +  +  +  + ]:        1188 :         if (nullsFirst && !reverse)
    2883                 :             :         {
    2884                 :           6 :                 appendStringInfoString(buf, " NULLS FIRST");
    2885                 :           6 :         }
    2886   [ +  +  +  - ]:        1182 :         else if (!nullsFirst && reverse)
    2887                 :             :         {
    2888                 :           0 :                 appendStringInfoString(buf, " NULLS LAST");
    2889                 :           0 :         }
    2890                 :        1188 : }
    2891                 :             : 
    2892                 :             : /*
    2893                 :             :  * Show the window definition for a WindowAgg node.
    2894                 :             :  */
    2895                 :             : static void
    2896                 :          77 : show_window_def(WindowAggState *planstate, List *ancestors, ExplainState *es)
    2897                 :             : {
    2898                 :          77 :         WindowAgg  *wagg = (WindowAgg *) planstate->ss.ps.plan;
    2899                 :          77 :         StringInfoData wbuf;
    2900                 :          77 :         bool            needspace = false;
    2901                 :             : 
    2902                 :          77 :         initStringInfo(&wbuf);
    2903                 :          77 :         appendStringInfo(&wbuf, "%s AS (", quote_identifier(wagg->winname));
    2904                 :             : 
    2905                 :             :         /* The key columns refer to the tlist of the child plan */
    2906                 :          77 :         ancestors = lcons(wagg, ancestors);
    2907         [ +  + ]:          77 :         if (wagg->partNumCols > 0)
    2908                 :             :         {
    2909                 :          40 :                 appendStringInfoString(&wbuf, "PARTITION BY ");
    2910                 :          80 :                 show_window_keys(&wbuf, outerPlanState(planstate),
    2911                 :          40 :                                                  wagg->partNumCols, wagg->partColIdx,
    2912                 :          40 :                                                  ancestors, es);
    2913                 :          40 :                 needspace = true;
    2914                 :          40 :         }
    2915         [ +  + ]:          77 :         if (wagg->ordNumCols > 0)
    2916                 :             :         {
    2917         [ +  + ]:          54 :                 if (needspace)
    2918                 :          26 :                         appendStringInfoChar(&wbuf, ' ');
    2919                 :          54 :                 appendStringInfoString(&wbuf, "ORDER BY ");
    2920                 :         108 :                 show_window_keys(&wbuf, outerPlanState(planstate),
    2921                 :          54 :                                                  wagg->ordNumCols, wagg->ordColIdx,
    2922                 :          54 :                                                  ancestors, es);
    2923                 :          54 :                 needspace = true;
    2924                 :          54 :         }
    2925                 :          77 :         ancestors = list_delete_first(ancestors);
    2926         [ +  + ]:          77 :         if (wagg->frameOptions & FRAMEOPTION_NONDEFAULT)
    2927                 :             :         {
    2928                 :          32 :                 List       *context;
    2929                 :          32 :                 bool            useprefix;
    2930                 :          32 :                 char       *framestr;
    2931                 :             : 
    2932                 :             :                 /* Set up deparsing context for possible frame expressions */
    2933                 :          64 :                 context = set_deparse_context_plan(es->deparse_cxt,
    2934                 :          32 :                                                                                    (Plan *) wagg,
    2935                 :          32 :                                                                                    ancestors);
    2936         [ +  + ]:          32 :                 useprefix = (es->rtable_size > 1 || es->verbose);
    2937                 :          64 :                 framestr = get_window_frame_options_for_explain(wagg->frameOptions,
    2938                 :          32 :                                                                                                                 wagg->startOffset,
    2939                 :          32 :                                                                                                                 wagg->endOffset,
    2940                 :          32 :                                                                                                                 context,
    2941                 :          32 :                                                                                                                 useprefix);
    2942         [ +  + ]:          32 :                 if (needspace)
    2943                 :          30 :                         appendStringInfoChar(&wbuf, ' ');
    2944                 :          32 :                 appendStringInfoString(&wbuf, framestr);
    2945                 :          32 :                 pfree(framestr);
    2946                 :          32 :         }
    2947                 :          77 :         appendStringInfoChar(&wbuf, ')');
    2948                 :          77 :         ExplainPropertyText("Window", wbuf.data, es);
    2949                 :          77 :         pfree(wbuf.data);
    2950                 :          77 : }
    2951                 :             : 
    2952                 :             : /*
    2953                 :             :  * Append the keys of a window's PARTITION BY or ORDER BY clause to buf.
    2954                 :             :  * We can't use show_sort_group_keys for this because that's too opinionated
    2955                 :             :  * about how the result will be displayed.
    2956                 :             :  * Note that the "planstate" node should be the WindowAgg's child.
    2957                 :             :  */
    2958                 :             : static void
    2959                 :          94 : show_window_keys(StringInfo buf, PlanState *planstate,
    2960                 :             :                                  int nkeys, AttrNumber *keycols,
    2961                 :             :                                  List *ancestors, ExplainState *es)
    2962                 :             : {
    2963                 :          94 :         Plan       *plan = planstate->plan;
    2964                 :          94 :         List       *context;
    2965                 :          94 :         bool            useprefix;
    2966                 :             : 
    2967                 :             :         /* Set up deparsing context */
    2968                 :         188 :         context = set_deparse_context_plan(es->deparse_cxt,
    2969                 :          94 :                                                                            plan,
    2970                 :          94 :                                                                            ancestors);
    2971         [ +  + ]:          94 :         useprefix = (es->rtable_size > 1 || es->verbose);
    2972                 :             : 
    2973         [ +  + ]:         193 :         for (int keyno = 0; keyno < nkeys; keyno++)
    2974                 :             :         {
    2975                 :             :                 /* find key expression in tlist */
    2976                 :          99 :                 AttrNumber      keyresno = keycols[keyno];
    2977                 :         198 :                 TargetEntry *target = get_tle_by_resno(plan->targetlist,
    2978                 :          99 :                                                                                            keyresno);
    2979                 :          99 :                 char       *exprstr;
    2980                 :             : 
    2981         [ +  - ]:          99 :                 if (!target)
    2982   [ #  #  #  # ]:           0 :                         elog(ERROR, "no tlist entry for key %d", keyresno);
    2983                 :             :                 /* Deparse the expression, showing any top-level cast */
    2984                 :         198 :                 exprstr = deparse_expression((Node *) target->expr, context,
    2985                 :          99 :                                                                          useprefix, true);
    2986         [ +  + ]:          99 :                 if (keyno > 0)
    2987                 :           5 :                         appendStringInfoString(buf, ", ");
    2988                 :          99 :                 appendStringInfoString(buf, exprstr);
    2989                 :          99 :                 pfree(exprstr);
    2990                 :             : 
    2991                 :             :                 /*
    2992                 :             :                  * We don't attempt to provide sort order information because
    2993                 :             :                  * WindowAgg carries equality operators not comparison operators;
    2994                 :             :                  * compare show_agg_keys.
    2995                 :             :                  */
    2996                 :          99 :         }
    2997                 :          94 : }
    2998                 :             : 
    2999                 :             : /*
    3000                 :             :  * Show information on storage method and maximum memory/disk space used.
    3001                 :             :  */
    3002                 :             : static void
    3003                 :           5 : show_storage_info(char *maxStorageType, int64 maxSpaceUsed, ExplainState *es)
    3004                 :             : {
    3005                 :           5 :         int64           maxSpaceUsedKB = BYTES_TO_KILOBYTES(maxSpaceUsed);
    3006                 :             : 
    3007         [ -  + ]:           5 :         if (es->format != EXPLAIN_FORMAT_TEXT)
    3008                 :             :         {
    3009                 :           0 :                 ExplainPropertyText("Storage", maxStorageType, es);
    3010                 :           0 :                 ExplainPropertyInteger("Maximum Storage", "kB", maxSpaceUsedKB, es);
    3011                 :           0 :         }
    3012                 :             :         else
    3013                 :             :         {
    3014                 :           5 :                 ExplainIndentText(es);
    3015                 :          10 :                 appendStringInfo(es->str,
    3016                 :             :                                                  "Storage: %s  Maximum Storage: " INT64_FORMAT "kB\n",
    3017                 :           5 :                                                  maxStorageType,
    3018                 :           5 :                                                  maxSpaceUsedKB);
    3019                 :             :         }
    3020                 :           5 : }
    3021                 :             : 
    3022                 :             : /*
    3023                 :             :  * Show TABLESAMPLE properties
    3024                 :             :  */
    3025                 :             : static void
    3026                 :          18 : show_tablesample(TableSampleClause *tsc, PlanState *planstate,
    3027                 :             :                                  List *ancestors, ExplainState *es)
    3028                 :             : {
    3029                 :          18 :         List       *context;
    3030                 :          18 :         bool            useprefix;
    3031                 :          18 :         char       *method_name;
    3032                 :          18 :         List       *params = NIL;
    3033                 :          18 :         char       *repeatable;
    3034                 :          18 :         ListCell   *lc;
    3035                 :             : 
    3036                 :             :         /* Set up deparsing context */
    3037                 :          36 :         context = set_deparse_context_plan(es->deparse_cxt,
    3038                 :          18 :                                                                            planstate->plan,
    3039                 :          18 :                                                                            ancestors);
    3040                 :          18 :         useprefix = es->rtable_size > 1;
    3041                 :             : 
    3042                 :             :         /* Get the tablesample method name */
    3043                 :          18 :         method_name = get_func_name(tsc->tsmhandler);
    3044                 :             : 
    3045                 :             :         /* Deparse parameter expressions */
    3046   [ +  -  +  +  :          36 :         foreach(lc, tsc->args)
                   +  + ]
    3047                 :             :         {
    3048                 :          18 :                 Node       *arg = (Node *) lfirst(lc);
    3049                 :             : 
    3050                 :          36 :                 params = lappend(params,
    3051                 :          36 :                                                  deparse_expression(arg, context,
    3052                 :          18 :                                                                                         useprefix, false));
    3053                 :          18 :         }
    3054         [ +  + ]:          18 :         if (tsc->repeatable)
    3055                 :          20 :                 repeatable = deparse_expression((Node *) tsc->repeatable, context,
    3056                 :          10 :                                                                                 useprefix, false);
    3057                 :             :         else
    3058                 :           8 :                 repeatable = NULL;
    3059                 :             : 
    3060                 :             :         /* Print results */
    3061         [ -  + ]:          18 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    3062                 :             :         {
    3063                 :          18 :                 bool            first = true;
    3064                 :             : 
    3065                 :          18 :                 ExplainIndentText(es);
    3066                 :          18 :                 appendStringInfo(es->str, "Sampling: %s (", method_name);
    3067   [ +  -  +  +  :          36 :                 foreach(lc, params)
                   +  + ]
    3068                 :             :                 {
    3069         [ +  - ]:          18 :                         if (!first)
    3070                 :           0 :                                 appendStringInfoString(es->str, ", ");
    3071                 :          18 :                         appendStringInfoString(es->str, (const char *) lfirst(lc));
    3072                 :          18 :                         first = false;
    3073                 :          18 :                 }
    3074                 :          18 :                 appendStringInfoChar(es->str, ')');
    3075         [ +  + ]:          18 :                 if (repeatable)
    3076                 :          10 :                         appendStringInfo(es->str, " REPEATABLE (%s)", repeatable);
    3077                 :          18 :                 appendStringInfoChar(es->str, '\n');
    3078                 :          18 :         }
    3079                 :             :         else
    3080                 :             :         {
    3081                 :           0 :                 ExplainPropertyText("Sampling Method", method_name, es);
    3082                 :           0 :                 ExplainPropertyList("Sampling Parameters", params, es);
    3083         [ #  # ]:           0 :                 if (repeatable)
    3084                 :           0 :                         ExplainPropertyText("Repeatable Seed", repeatable, es);
    3085                 :             :         }
    3086                 :          18 : }
    3087                 :             : 
    3088                 :             : /*
    3089                 :             :  * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
    3090                 :             :  */
    3091                 :             : static void
    3092                 :         659 : show_sort_info(SortState *sortstate, ExplainState *es)
    3093                 :             : {
    3094         [ +  + ]:         659 :         if (!es->analyze)
    3095                 :         632 :                 return;
    3096                 :             : 
    3097   [ +  +  -  + ]:          27 :         if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
    3098                 :             :         {
    3099                 :          26 :                 Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
    3100                 :          26 :                 TuplesortInstrumentation stats;
    3101                 :          26 :                 const char *sortMethod;
    3102                 :          26 :                 const char *spaceType;
    3103                 :          26 :                 int64           spaceUsed;
    3104                 :             : 
    3105                 :          26 :                 tuplesort_get_stats(state, &stats);
    3106                 :          26 :                 sortMethod = tuplesort_method_name(stats.sortMethod);
    3107                 :          26 :                 spaceType = tuplesort_space_type_name(stats.spaceType);
    3108                 :          26 :                 spaceUsed = stats.spaceUsed;
    3109                 :             : 
    3110         [ +  + ]:          26 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    3111                 :             :                 {
    3112                 :          21 :                         ExplainIndentText(es);
    3113                 :          42 :                         appendStringInfo(es->str, "Sort Method: %s  %s: " INT64_FORMAT "kB\n",
    3114                 :          21 :                                                          sortMethod, spaceType, spaceUsed);
    3115                 :          21 :                 }
    3116                 :             :                 else
    3117                 :             :                 {
    3118                 :           5 :                         ExplainPropertyText("Sort Method", sortMethod, es);
    3119                 :           5 :                         ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
    3120                 :           5 :                         ExplainPropertyText("Sort Space Type", spaceType, es);
    3121                 :             :                 }
    3122                 :          26 :         }
    3123                 :             : 
    3124                 :             :         /*
    3125                 :             :          * You might think we should just skip this stanza entirely when
    3126                 :             :          * es->hide_workers is true, but then we'd get no sort-method output at
    3127                 :             :          * all.  We have to make it look like worker 0's data is top-level data.
    3128                 :             :          * This is easily done by just skipping the OpenWorker/CloseWorker calls.
    3129                 :             :          * Currently, we don't worry about the possibility that there are multiple
    3130                 :             :          * workers in such a case; if there are, duplicate output fields will be
    3131                 :             :          * emitted.
    3132                 :             :          */
    3133         [ +  + ]:          27 :         if (sortstate->shared_info != NULL)
    3134                 :             :         {
    3135                 :           2 :                 int                     n;
    3136                 :             : 
    3137         [ +  + ]:          10 :                 for (n = 0; n < sortstate->shared_info->num_workers; n++)
    3138                 :             :                 {
    3139                 :           8 :                         TuplesortInstrumentation *sinstrument;
    3140                 :           8 :                         const char *sortMethod;
    3141                 :           8 :                         const char *spaceType;
    3142                 :           8 :                         int64           spaceUsed;
    3143                 :             : 
    3144                 :           8 :                         sinstrument = &sortstate->shared_info->sinstrument[n];
    3145         [ +  - ]:           8 :                         if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS)
    3146                 :           0 :                                 continue;               /* ignore any unfilled slots */
    3147                 :           8 :                         sortMethod = tuplesort_method_name(sinstrument->sortMethod);
    3148                 :           8 :                         spaceType = tuplesort_space_type_name(sinstrument->spaceType);
    3149                 :           8 :                         spaceUsed = sinstrument->spaceUsed;
    3150                 :             : 
    3151         [ -  + ]:           8 :                         if (es->workers_state)
    3152                 :           8 :                                 ExplainOpenWorker(n, es);
    3153                 :             : 
    3154         [ +  + ]:           8 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    3155                 :             :                         {
    3156                 :           4 :                                 ExplainIndentText(es);
    3157                 :           8 :                                 appendStringInfo(es->str,
    3158                 :             :                                                                  "Sort Method: %s  %s: " INT64_FORMAT "kB\n",
    3159                 :           4 :                                                                  sortMethod, spaceType, spaceUsed);
    3160                 :           4 :                         }
    3161                 :             :                         else
    3162                 :             :                         {
    3163                 :           4 :                                 ExplainPropertyText("Sort Method", sortMethod, es);
    3164                 :           4 :                                 ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
    3165                 :           4 :                                 ExplainPropertyText("Sort Space Type", spaceType, es);
    3166                 :             :                         }
    3167                 :             : 
    3168         [ -  + ]:           8 :                         if (es->workers_state)
    3169                 :           8 :                                 ExplainCloseWorker(n, es);
    3170      [ -  -  + ]:           8 :                 }
    3171                 :           2 :         }
    3172                 :         659 : }
    3173                 :             : 
    3174                 :             : /*
    3175                 :             :  * Incremental sort nodes sort in (a potentially very large number of) batches,
    3176                 :             :  * so EXPLAIN ANALYZE needs to roll up the tuplesort stats from each batch into
    3177                 :             :  * an intelligible summary.
    3178                 :             :  *
    3179                 :             :  * This function is used for both a non-parallel node and each worker in a
    3180                 :             :  * parallel incremental sort node.
    3181                 :             :  */
    3182                 :             : static void
    3183                 :           9 : show_incremental_sort_group_info(IncrementalSortGroupInfo *groupInfo,
    3184                 :             :                                                                  const char *groupLabel, bool indent, ExplainState *es)
    3185                 :             : {
    3186                 :           9 :         ListCell   *methodCell;
    3187                 :           9 :         List       *methodNames = NIL;
    3188                 :             : 
    3189                 :             :         /* Generate a list of sort methods used across all groups. */
    3190         [ +  + ]:          45 :         for (int bit = 0; bit < NUM_TUPLESORTMETHODS; bit++)
    3191                 :             :         {
    3192                 :          36 :                 TuplesortMethod sortMethod = (1 << bit);
    3193                 :             : 
    3194         [ +  + ]:          36 :                 if (groupInfo->sortMethods & sortMethod)
    3195                 :             :                 {
    3196                 :          15 :                         const char *methodName = tuplesort_method_name(sortMethod);
    3197                 :             : 
    3198                 :          15 :                         methodNames = lappend(methodNames, unconstify(char *, methodName));
    3199                 :          15 :                 }
    3200                 :          36 :         }
    3201                 :             : 
    3202         [ +  + ]:           9 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    3203                 :             :         {
    3204         [ -  + ]:           3 :                 if (indent)
    3205                 :           3 :                         appendStringInfoSpaces(es->str, es->indent * 2);
    3206                 :           6 :                 appendStringInfo(es->str, "%s Groups: " INT64_FORMAT "  Sort Method", groupLabel,
    3207                 :           3 :                                                  groupInfo->groupCount);
    3208                 :             :                 /* plural/singular based on methodNames size */
    3209         [ +  + ]:           3 :                 if (list_length(methodNames) > 1)
    3210                 :           2 :                         appendStringInfoString(es->str, "s: ");
    3211                 :             :                 else
    3212                 :           1 :                         appendStringInfoString(es->str, ": ");
    3213   [ +  -  +  +  :           8 :                 foreach(methodCell, methodNames)
                   +  + ]
    3214                 :             :                 {
    3215                 :           5 :                         appendStringInfoString(es->str, (char *) methodCell->ptr_value);
    3216         [ +  + ]:           5 :                         if (foreach_current_index(methodCell) < list_length(methodNames) - 1)
    3217                 :           2 :                                 appendStringInfoString(es->str, ", ");
    3218                 :           5 :                 }
    3219                 :             : 
    3220         [ -  + ]:           3 :                 if (groupInfo->maxMemorySpaceUsed > 0)
    3221                 :             :                 {
    3222                 :           3 :                         int64           avgSpace = groupInfo->totalMemorySpaceUsed / groupInfo->groupCount;
    3223                 :           3 :                         const char *spaceTypeName;
    3224                 :             : 
    3225                 :           3 :                         spaceTypeName = tuplesort_space_type_name(SORT_SPACE_TYPE_MEMORY);
    3226                 :           6 :                         appendStringInfo(es->str, "  Average %s: " INT64_FORMAT "kB  Peak %s: " INT64_FORMAT "kB",
    3227                 :           3 :                                                          spaceTypeName, avgSpace,
    3228                 :           3 :                                                          spaceTypeName, groupInfo->maxMemorySpaceUsed);
    3229                 :           3 :                 }
    3230                 :             : 
    3231         [ +  - ]:           3 :                 if (groupInfo->maxDiskSpaceUsed > 0)
    3232                 :             :                 {
    3233                 :           0 :                         int64           avgSpace = groupInfo->totalDiskSpaceUsed / groupInfo->groupCount;
    3234                 :             : 
    3235                 :           0 :                         const char *spaceTypeName;
    3236                 :             : 
    3237                 :           0 :                         spaceTypeName = tuplesort_space_type_name(SORT_SPACE_TYPE_DISK);
    3238                 :           0 :                         appendStringInfo(es->str, "  Average %s: " INT64_FORMAT "kB  Peak %s: " INT64_FORMAT "kB",
    3239                 :           0 :                                                          spaceTypeName, avgSpace,
    3240                 :           0 :                                                          spaceTypeName, groupInfo->maxDiskSpaceUsed);
    3241                 :           0 :                 }
    3242                 :           3 :         }
    3243                 :             :         else
    3244                 :             :         {
    3245                 :           6 :                 StringInfoData groupName;
    3246                 :             : 
    3247                 :           6 :                 initStringInfo(&groupName);
    3248                 :           6 :                 appendStringInfo(&groupName, "%s Groups", groupLabel);
    3249                 :           6 :                 ExplainOpenGroup("Incremental Sort Groups", groupName.data, true, es);
    3250                 :           6 :                 ExplainPropertyInteger("Group Count", NULL, groupInfo->groupCount, es);
    3251                 :             : 
    3252                 :           6 :                 ExplainPropertyList("Sort Methods Used", methodNames, es);
    3253                 :             : 
    3254         [ -  + ]:           6 :                 if (groupInfo->maxMemorySpaceUsed > 0)
    3255                 :             :                 {
    3256                 :           6 :                         int64           avgSpace = groupInfo->totalMemorySpaceUsed / groupInfo->groupCount;
    3257                 :           6 :                         const char *spaceTypeName;
    3258                 :           6 :                         StringInfoData memoryName;
    3259                 :             : 
    3260                 :           6 :                         spaceTypeName = tuplesort_space_type_name(SORT_SPACE_TYPE_MEMORY);
    3261                 :           6 :                         initStringInfo(&memoryName);
    3262                 :           6 :                         appendStringInfo(&memoryName, "Sort Space %s", spaceTypeName);
    3263                 :           6 :                         ExplainOpenGroup("Sort Space", memoryName.data, true, es);
    3264                 :             : 
    3265                 :           6 :                         ExplainPropertyInteger("Average Sort Space Used", "kB", avgSpace, es);
    3266                 :           6 :                         ExplainPropertyInteger("Peak Sort Space Used", "kB",
    3267                 :           6 :                                                                    groupInfo->maxMemorySpaceUsed, es);
    3268                 :             : 
    3269                 :           6 :                         ExplainCloseGroup("Sort Space", memoryName.data, true, es);
    3270                 :           6 :                 }
    3271         [ +  - ]:           6 :                 if (groupInfo->maxDiskSpaceUsed > 0)
    3272                 :             :                 {
    3273                 :           0 :                         int64           avgSpace = groupInfo->totalDiskSpaceUsed / groupInfo->groupCount;
    3274                 :           0 :                         const char *spaceTypeName;
    3275                 :           0 :                         StringInfoData diskName;
    3276                 :             : 
    3277                 :           0 :                         spaceTypeName = tuplesort_space_type_name(SORT_SPACE_TYPE_DISK);
    3278                 :           0 :                         initStringInfo(&diskName);
    3279                 :           0 :                         appendStringInfo(&diskName, "Sort Space %s", spaceTypeName);
    3280                 :           0 :                         ExplainOpenGroup("Sort Space", diskName.data, true, es);
    3281                 :             : 
    3282                 :           0 :                         ExplainPropertyInteger("Average Sort Space Used", "kB", avgSpace, es);
    3283                 :           0 :                         ExplainPropertyInteger("Peak Sort Space Used", "kB",
    3284                 :           0 :                                                                    groupInfo->maxDiskSpaceUsed, es);
    3285                 :             : 
    3286                 :           0 :                         ExplainCloseGroup("Sort Space", diskName.data, true, es);
    3287                 :           0 :                 }
    3288                 :             : 
    3289                 :           6 :                 ExplainCloseGroup("Incremental Sort Groups", groupName.data, true, es);
    3290                 :           6 :         }
    3291                 :           9 : }
    3292                 :             : 
    3293                 :             : /*
    3294                 :             :  * If it's EXPLAIN ANALYZE, show tuplesort stats for an incremental sort node
    3295                 :             :  */
    3296                 :             : static void
    3297                 :          64 : show_incremental_sort_info(IncrementalSortState *incrsortstate,
    3298                 :             :                                                    ExplainState *es)
    3299                 :             : {
    3300                 :          64 :         IncrementalSortGroupInfo *fullsortGroupInfo;
    3301                 :          64 :         IncrementalSortGroupInfo *prefixsortGroupInfo;
    3302                 :             : 
    3303                 :          64 :         fullsortGroupInfo = &incrsortstate->incsort_info.fullsortGroupInfo;
    3304                 :             : 
    3305         [ +  + ]:          64 :         if (!es->analyze)
    3306                 :          58 :                 return;
    3307                 :             : 
    3308                 :             :         /*
    3309                 :             :          * Since we never have any prefix groups unless we've first sorted a full
    3310                 :             :          * groups and transitioned modes (copying the tuples into a prefix group),
    3311                 :             :          * we don't need to do anything if there were 0 full groups.
    3312                 :             :          *
    3313                 :             :          * We still have to continue after this block if there are no full groups,
    3314                 :             :          * though, since it's possible that we have workers that did real work
    3315                 :             :          * even if the leader didn't participate.
    3316                 :             :          */
    3317         [ +  - ]:           6 :         if (fullsortGroupInfo->groupCount > 0)
    3318                 :             :         {
    3319                 :           6 :                 show_incremental_sort_group_info(fullsortGroupInfo, "Full-sort", true, es);
    3320                 :           6 :                 prefixsortGroupInfo = &incrsortstate->incsort_info.prefixsortGroupInfo;
    3321         [ +  + ]:           6 :                 if (prefixsortGroupInfo->groupCount > 0)
    3322                 :             :                 {
    3323         [ +  + ]:           3 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    3324                 :           1 :                                 appendStringInfoChar(es->str, '\n');
    3325                 :           3 :                         show_incremental_sort_group_info(prefixsortGroupInfo, "Pre-sorted", true, es);
    3326                 :           3 :                 }
    3327         [ +  + ]:           6 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    3328                 :           2 :                         appendStringInfoChar(es->str, '\n');
    3329                 :           6 :         }
    3330                 :             : 
    3331         [ +  - ]:           6 :         if (incrsortstate->shared_info != NULL)
    3332                 :             :         {
    3333                 :           0 :                 int                     n;
    3334                 :           0 :                 bool            indent_first_line;
    3335                 :             : 
    3336         [ #  # ]:           0 :                 for (n = 0; n < incrsortstate->shared_info->num_workers; n++)
    3337                 :             :                 {
    3338                 :           0 :                         IncrementalSortInfo *incsort_info =
    3339                 :           0 :                                 &incrsortstate->shared_info->sinfo[n];
    3340                 :             : 
    3341                 :             :                         /*
    3342                 :             :                          * If a worker hasn't processed any sort groups at all, then
    3343                 :             :                          * exclude it from output since it either didn't launch or didn't
    3344                 :             :                          * contribute anything meaningful.
    3345                 :             :                          */
    3346                 :           0 :                         fullsortGroupInfo = &incsort_info->fullsortGroupInfo;
    3347                 :             : 
    3348                 :             :                         /*
    3349                 :             :                          * Since we never have any prefix groups unless we've first sorted
    3350                 :             :                          * a full groups and transitioned modes (copying the tuples into a
    3351                 :             :                          * prefix group), we don't need to do anything if there were 0
    3352                 :             :                          * full groups.
    3353                 :             :                          */
    3354         [ #  # ]:           0 :                         if (fullsortGroupInfo->groupCount == 0)
    3355                 :           0 :                                 continue;
    3356                 :             : 
    3357         [ #  # ]:           0 :                         if (es->workers_state)
    3358                 :           0 :                                 ExplainOpenWorker(n, es);
    3359                 :             : 
    3360         [ #  # ]:           0 :                         indent_first_line = es->workers_state == NULL || es->verbose;
    3361                 :           0 :                         show_incremental_sort_group_info(fullsortGroupInfo, "Full-sort",
    3362                 :           0 :                                                                                          indent_first_line, es);
    3363                 :           0 :                         prefixsortGroupInfo = &incsort_info->prefixsortGroupInfo;
    3364         [ #  # ]:           0 :                         if (prefixsortGroupInfo->groupCount > 0)
    3365                 :             :                         {
    3366         [ #  # ]:           0 :                                 if (es->format == EXPLAIN_FORMAT_TEXT)
    3367                 :           0 :                                         appendStringInfoChar(es->str, '\n');
    3368                 :           0 :                                 show_incremental_sort_group_info(prefixsortGroupInfo, "Pre-sorted", true, es);
    3369                 :           0 :                         }
    3370         [ #  # ]:           0 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    3371                 :           0 :                                 appendStringInfoChar(es->str, '\n');
    3372                 :             : 
    3373         [ #  # ]:           0 :                         if (es->workers_state)
    3374                 :           0 :                                 ExplainCloseWorker(n, es);
    3375         [ #  # ]:           0 :                 }
    3376                 :           0 :         }
    3377                 :          64 : }
    3378                 :             : 
    3379                 :             : /*
    3380                 :             :  * Show information on hash buckets/batches.
    3381                 :             :  */
    3382                 :             : static void
    3383                 :         627 : show_hash_info(HashState *hashstate, ExplainState *es)
    3384                 :             : {
    3385                 :         627 :         HashInstrumentation hinstrument = {0};
    3386                 :             : 
    3387                 :             :         /*
    3388                 :             :          * Collect stats from the local process, even when it's a parallel query.
    3389                 :             :          * In a parallel query, the leader process may or may not have run the
    3390                 :             :          * hash join, and even if it did it may not have built a hash table due to
    3391                 :             :          * timing (if it started late it might have seen no tuples in the outer
    3392                 :             :          * relation and skipped building the hash table).  Therefore we have to be
    3393                 :             :          * prepared to get instrumentation data from all participants.
    3394                 :             :          */
    3395         [ +  + ]:         627 :         if (hashstate->hinstrument)
    3396                 :          19 :                 memcpy(&hinstrument, hashstate->hinstrument,
    3397                 :             :                            sizeof(HashInstrumentation));
    3398                 :             : 
    3399                 :             :         /*
    3400                 :             :          * Merge results from workers.  In the parallel-oblivious case, the
    3401                 :             :          * results from all participants should be identical, except where
    3402                 :             :          * participants didn't run the join at all so have no data.  In the
    3403                 :             :          * parallel-aware case, we need to consider all the results.  Each worker
    3404                 :             :          * may have seen a different subset of batches and we want to report the
    3405                 :             :          * highest memory usage across all batches.  We take the maxima of other
    3406                 :             :          * values too, for the same reasons as in ExecHashAccumInstrumentation.
    3407                 :             :          */
    3408         [ +  + ]:         627 :         if (hashstate->shared_info)
    3409                 :             :         {
    3410                 :          14 :                 SharedHashInfo *shared_info = hashstate->shared_info;
    3411                 :          14 :                 int                     i;
    3412                 :             : 
    3413         [ +  + ]:          40 :                 for (i = 0; i < shared_info->num_workers; ++i)
    3414                 :             :                 {
    3415                 :          26 :                         HashInstrumentation *worker_hi = &shared_info->hinstrument[i];
    3416                 :             : 
    3417         [ -  + ]:          26 :                         hinstrument.nbuckets = Max(hinstrument.nbuckets,
    3418                 :             :                                                                            worker_hi->nbuckets);
    3419         [ -  + ]:          26 :                         hinstrument.nbuckets_original = Max(hinstrument.nbuckets_original,
    3420                 :             :                                                                                                 worker_hi->nbuckets_original);
    3421         [ -  + ]:          26 :                         hinstrument.nbatch = Max(hinstrument.nbatch,
    3422                 :             :                                                                          worker_hi->nbatch);
    3423         [ -  + ]:          26 :                         hinstrument.nbatch_original = Max(hinstrument.nbatch_original,
    3424                 :             :                                                                                           worker_hi->nbatch_original);
    3425         [ +  + ]:          26 :                         hinstrument.space_peak = Max(hinstrument.space_peak,
    3426                 :             :                                                                                  worker_hi->space_peak);
    3427                 :          26 :                 }
    3428                 :          14 :         }
    3429                 :             : 
    3430         [ +  + ]:         627 :         if (hinstrument.nbatch > 0)
    3431                 :             :         {
    3432                 :          19 :                 uint64          spacePeakKb = BYTES_TO_KILOBYTES(hinstrument.space_peak);
    3433                 :             : 
    3434         [ +  + ]:          19 :                 if (es->format != EXPLAIN_FORMAT_TEXT)
    3435                 :             :                 {
    3436                 :          18 :                         ExplainPropertyInteger("Hash Buckets", NULL,
    3437                 :          18 :                                                                    hinstrument.nbuckets, es);
    3438                 :          18 :                         ExplainPropertyInteger("Original Hash Buckets", NULL,
    3439                 :          18 :                                                                    hinstrument.nbuckets_original, es);
    3440                 :          18 :                         ExplainPropertyInteger("Hash Batches", NULL,
    3441                 :          18 :                                                                    hinstrument.nbatch, es);
    3442                 :          18 :                         ExplainPropertyInteger("Original Hash Batches", NULL,
    3443                 :          18 :                                                                    hinstrument.nbatch_original, es);
    3444                 :          18 :                         ExplainPropertyUInteger("Peak Memory Usage", "kB",
    3445                 :          18 :                                                                         spacePeakKb, es);
    3446                 :          18 :                 }
    3447   [ +  -  -  + ]:           1 :                 else if (hinstrument.nbatch_original != hinstrument.nbatch ||
    3448                 :           1 :                                  hinstrument.nbuckets_original != hinstrument.nbuckets)
    3449                 :             :                 {
    3450                 :           0 :                         ExplainIndentText(es);
    3451                 :           0 :                         appendStringInfo(es->str,
    3452                 :             :                                                          "Buckets: %d (originally %d)  Batches: %d (originally %d)  Memory Usage: " UINT64_FORMAT "kB\n",
    3453                 :           0 :                                                          hinstrument.nbuckets,
    3454                 :           0 :                                                          hinstrument.nbuckets_original,
    3455                 :           0 :                                                          hinstrument.nbatch,
    3456                 :           0 :                                                          hinstrument.nbatch_original,
    3457                 :           0 :                                                          spacePeakKb);
    3458                 :           0 :                 }
    3459                 :             :                 else
    3460                 :             :                 {
    3461                 :           1 :                         ExplainIndentText(es);
    3462                 :           2 :                         appendStringInfo(es->str,
    3463                 :             :                                                          "Buckets: %d  Batches: %d  Memory Usage: " UINT64_FORMAT "kB\n",
    3464                 :           1 :                                                          hinstrument.nbuckets, hinstrument.nbatch,
    3465                 :           1 :                                                          spacePeakKb);
    3466                 :             :                 }
    3467                 :          19 :         }
    3468                 :         627 : }
    3469                 :             : 
    3470                 :             : /*
    3471                 :             :  * Show information on material node, storage method and maximum memory/disk
    3472                 :             :  * space used.
    3473                 :             :  */
    3474                 :             : static void
    3475                 :         179 : show_material_info(MaterialState *mstate, ExplainState *es)
    3476                 :             : {
    3477                 :         179 :         char       *maxStorageType;
    3478                 :         179 :         int64           maxSpaceUsed;
    3479                 :             : 
    3480                 :         179 :         Tuplestorestate *tupstore = mstate->tuplestorestate;
    3481                 :             : 
    3482                 :             :         /*
    3483                 :             :          * Nothing to show if ANALYZE option wasn't used or if execution didn't
    3484                 :             :          * get as far as creating the tuplestore.
    3485                 :             :          */
    3486   [ +  +  -  + ]:         179 :         if (!es->analyze || tupstore == NULL)
    3487                 :         177 :                 return;
    3488                 :             : 
    3489                 :           2 :         tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
    3490                 :           2 :         show_storage_info(maxStorageType, maxSpaceUsed, es);
    3491         [ -  + ]:         179 : }
    3492                 :             : 
    3493                 :             : /*
    3494                 :             :  * Show information on WindowAgg node, storage method and maximum memory/disk
    3495                 :             :  * space used.
    3496                 :             :  */
    3497                 :             : static void
    3498                 :          77 : show_windowagg_info(WindowAggState *winstate, ExplainState *es)
    3499                 :             : {
    3500                 :          77 :         char       *maxStorageType;
    3501                 :          77 :         int64           maxSpaceUsed;
    3502                 :             : 
    3503                 :          77 :         Tuplestorestate *tupstore = winstate->buffer;
    3504                 :             : 
    3505                 :             :         /*
    3506                 :             :          * Nothing to show if ANALYZE option wasn't used or if execution didn't
    3507                 :             :          * get as far as creating the tuplestore.
    3508                 :             :          */
    3509   [ +  +  -  + ]:          77 :         if (!es->analyze || tupstore == NULL)
    3510                 :          74 :                 return;
    3511                 :             : 
    3512                 :           3 :         tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
    3513                 :           3 :         show_storage_info(maxStorageType, maxSpaceUsed, es);
    3514         [ -  + ]:          77 : }
    3515                 :             : 
    3516                 :             : /*
    3517                 :             :  * Show information on CTE Scan node, storage method and maximum memory/disk
    3518                 :             :  * space used.
    3519                 :             :  */
    3520                 :             : static void
    3521                 :          41 : show_ctescan_info(CteScanState *ctescanstate, ExplainState *es)
    3522                 :             : {
    3523                 :          41 :         char       *maxStorageType;
    3524                 :          41 :         int64           maxSpaceUsed;
    3525                 :             : 
    3526                 :          41 :         Tuplestorestate *tupstore = ctescanstate->leader->cte_table;
    3527                 :             : 
    3528   [ -  +  #  # ]:          41 :         if (!es->analyze || tupstore == NULL)
    3529                 :          41 :                 return;
    3530                 :             : 
    3531                 :           0 :         tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
    3532                 :           0 :         show_storage_info(maxStorageType, maxSpaceUsed, es);
    3533         [ -  + ]:          41 : }
    3534                 :             : 
    3535                 :             : /*
    3536                 :             :  * Show information on Table Function Scan node, storage method and maximum
    3537                 :             :  * memory/disk space used.
    3538                 :             :  */
    3539                 :             : static void
    3540                 :          13 : show_table_func_scan_info(TableFuncScanState *tscanstate, ExplainState *es)
    3541                 :             : {
    3542                 :          13 :         char       *maxStorageType;
    3543                 :          13 :         int64           maxSpaceUsed;
    3544                 :             : 
    3545                 :          13 :         Tuplestorestate *tupstore = tscanstate->tupstore;
    3546                 :             : 
    3547   [ -  +  #  # ]:          13 :         if (!es->analyze || tupstore == NULL)
    3548                 :          13 :                 return;
    3549                 :             : 
    3550                 :           0 :         tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed);
    3551                 :           0 :         show_storage_info(maxStorageType, maxSpaceUsed, es);
    3552         [ -  + ]:          13 : }
    3553                 :             : 
    3554                 :             : /*
    3555                 :             :  * Show information on Recursive Union node, storage method and maximum
    3556                 :             :  * memory/disk space used.
    3557                 :             :  */
    3558                 :             : static void
    3559                 :           9 : show_recursive_union_info(RecursiveUnionState *rstate, ExplainState *es)
    3560                 :             : {
    3561                 :           9 :         char       *maxStorageType,
    3562                 :             :                            *tempStorageType;
    3563                 :           9 :         int64           maxSpaceUsed,
    3564                 :             :                                 tempSpaceUsed;
    3565                 :             : 
    3566         [ -  + ]:           9 :         if (!es->analyze)
    3567                 :           9 :                 return;
    3568                 :             : 
    3569                 :             :         /*
    3570                 :             :          * Recursive union node uses two tuplestores.  We employ the storage type
    3571                 :             :          * from one of them which consumed more memory/disk than the other.  The
    3572                 :             :          * storage size is sum of the two.
    3573                 :             :          */
    3574                 :           0 :         tuplestore_get_stats(rstate->working_table, &tempStorageType,
    3575                 :             :                                                  &tempSpaceUsed);
    3576                 :           0 :         tuplestore_get_stats(rstate->intermediate_table, &maxStorageType,
    3577                 :             :                                                  &maxSpaceUsed);
    3578                 :             : 
    3579         [ #  # ]:           0 :         if (tempSpaceUsed > maxSpaceUsed)
    3580                 :           0 :                 maxStorageType = tempStorageType;
    3581                 :             : 
    3582                 :           0 :         maxSpaceUsed += tempSpaceUsed;
    3583                 :           0 :         show_storage_info(maxStorageType, maxSpaceUsed, es);
    3584         [ -  + ]:           9 : }
    3585                 :             : 
    3586                 :             : /*
    3587                 :             :  * Show information on memoize hits/misses/evictions and memory usage.
    3588                 :             :  */
    3589                 :             : static void
    3590                 :          41 : show_memoize_info(MemoizeState *mstate, List *ancestors, ExplainState *es)
    3591                 :             : {
    3592                 :          41 :         Plan       *plan = ((PlanState *) mstate)->plan;
    3593                 :          41 :         Memoize    *mplan = (Memoize *) plan;
    3594                 :          41 :         ListCell   *lc;
    3595                 :          41 :         List       *context;
    3596                 :          41 :         StringInfoData keystr;
    3597                 :          41 :         char       *separator = "";
    3598                 :          41 :         bool            useprefix;
    3599                 :          41 :         int64           memPeakKb;
    3600                 :             : 
    3601                 :          41 :         initStringInfo(&keystr);
    3602                 :             : 
    3603                 :             :         /*
    3604                 :             :          * It's hard to imagine having a memoize node with fewer than 2 RTEs, but
    3605                 :             :          * let's just keep the same useprefix logic as elsewhere in this file.
    3606                 :             :          */
    3607         [ +  - ]:          41 :         useprefix = es->rtable_size > 1 || es->verbose;
    3608                 :             : 
    3609                 :             :         /* Set up deparsing context */
    3610                 :          82 :         context = set_deparse_context_plan(es->deparse_cxt,
    3611                 :          41 :                                                                            plan,
    3612                 :          41 :                                                                            ancestors);
    3613                 :             : 
    3614   [ +  -  +  +  :          88 :         foreach(lc, mplan->param_exprs)
                   +  + ]
    3615                 :             :         {
    3616                 :          47 :                 Node       *expr = (Node *) lfirst(lc);
    3617                 :             : 
    3618                 :          47 :                 appendStringInfoString(&keystr, separator);
    3619                 :             : 
    3620                 :          94 :                 appendStringInfoString(&keystr, deparse_expression(expr, context,
    3621                 :          47 :                                                                                                                    useprefix, false));
    3622                 :          47 :                 separator = ", ";
    3623                 :          47 :         }
    3624                 :             : 
    3625                 :          41 :         ExplainPropertyText("Cache Key", keystr.data, es);
    3626                 :          41 :         ExplainPropertyText("Cache Mode", mstate->binary_mode ? "binary" : "logical", es);
    3627                 :             : 
    3628                 :          41 :         pfree(keystr.data);
    3629                 :             : 
    3630         [ +  - ]:          41 :         if (es->costs)
    3631                 :             :         {
    3632         [ #  # ]:           0 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    3633                 :             :                 {
    3634                 :           0 :                         ExplainIndentText(es);
    3635                 :           0 :                         appendStringInfo(es->str, "Estimates: capacity=%u distinct keys=%.0f lookups=%.0f hit percent=%.2f%%\n",
    3636                 :           0 :                                                          mplan->est_entries, mplan->est_unique_keys,
    3637                 :           0 :                                                          mplan->est_calls, mplan->est_hit_ratio * 100.0);
    3638                 :           0 :                 }
    3639                 :             :                 else
    3640                 :             :                 {
    3641                 :           0 :                         ExplainPropertyUInteger("Estimated Capacity", NULL, mplan->est_entries, es);
    3642                 :           0 :                         ExplainPropertyFloat("Estimated Distinct Lookup Keys", NULL, mplan->est_unique_keys, 0, es);
    3643                 :           0 :                         ExplainPropertyFloat("Estimated Lookups", NULL, mplan->est_calls, 0, es);
    3644                 :           0 :                         ExplainPropertyFloat("Estimated Hit Percent", NULL, mplan->est_hit_ratio * 100.0, 2, es);
    3645                 :             :                 }
    3646                 :           0 :         }
    3647                 :             : 
    3648         [ +  + ]:          41 :         if (!es->analyze)
    3649                 :          26 :                 return;
    3650                 :             : 
    3651         [ -  + ]:          15 :         if (mstate->stats.cache_misses > 0)
    3652                 :             :         {
    3653                 :             :                 /*
    3654                 :             :                  * mem_peak is only set when we freed memory, so we must use mem_used
    3655                 :             :                  * when mem_peak is 0.
    3656                 :             :                  */
    3657         [ +  + ]:          15 :                 if (mstate->stats.mem_peak > 0)
    3658                 :           1 :                         memPeakKb = BYTES_TO_KILOBYTES(mstate->stats.mem_peak);
    3659                 :             :                 else
    3660                 :          14 :                         memPeakKb = BYTES_TO_KILOBYTES(mstate->mem_used);
    3661                 :             : 
    3662         [ -  + ]:          15 :                 if (es->format != EXPLAIN_FORMAT_TEXT)
    3663                 :             :                 {
    3664                 :           0 :                         ExplainPropertyInteger("Cache Hits", NULL, mstate->stats.cache_hits, es);
    3665                 :           0 :                         ExplainPropertyInteger("Cache Misses", NULL, mstate->stats.cache_misses, es);
    3666                 :           0 :                         ExplainPropertyInteger("Cache Evictions", NULL, mstate->stats.cache_evictions, es);
    3667                 :           0 :                         ExplainPropertyInteger("Cache Overflows", NULL, mstate->stats.cache_overflows, es);
    3668                 :           0 :                         ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
    3669                 :           0 :                 }
    3670                 :             :                 else
    3671                 :             :                 {
    3672                 :          15 :                         ExplainIndentText(es);
    3673                 :          30 :                         appendStringInfo(es->str,
    3674                 :             :                                                          "Hits: " UINT64_FORMAT "  Misses: " UINT64_FORMAT "  Evictions: " UINT64_FORMAT "  Overflows: " UINT64_FORMAT "  Memory Usage: " INT64_FORMAT "kB\n",
    3675                 :          15 :                                                          mstate->stats.cache_hits,
    3676                 :          15 :                                                          mstate->stats.cache_misses,
    3677                 :          15 :                                                          mstate->stats.cache_evictions,
    3678                 :          15 :                                                          mstate->stats.cache_overflows,
    3679                 :          15 :                                                          memPeakKb);
    3680                 :             :                 }
    3681                 :          15 :         }
    3682                 :             : 
    3683         [ -  + ]:          15 :         if (mstate->shared_info == NULL)
    3684                 :          15 :                 return;
    3685                 :             : 
    3686                 :             :         /* Show details from parallel workers */
    3687         [ #  # ]:           0 :         for (int n = 0; n < mstate->shared_info->num_workers; n++)
    3688                 :             :         {
    3689                 :           0 :                 MemoizeInstrumentation *si;
    3690                 :             : 
    3691                 :           0 :                 si = &mstate->shared_info->sinstrument[n];
    3692                 :             : 
    3693                 :             :                 /*
    3694                 :             :                  * Skip workers that didn't do any work.  We needn't bother checking
    3695                 :             :                  * for cache hits as a miss will always occur before a cache hit.
    3696                 :             :                  */
    3697         [ #  # ]:           0 :                 if (si->cache_misses == 0)
    3698                 :           0 :                         continue;
    3699                 :             : 
    3700         [ #  # ]:           0 :                 if (es->workers_state)
    3701                 :           0 :                         ExplainOpenWorker(n, es);
    3702                 :             : 
    3703                 :             :                 /*
    3704                 :             :                  * Since the worker's MemoizeState.mem_used field is unavailable to
    3705                 :             :                  * us, ExecEndMemoize will have set the
    3706                 :             :                  * MemoizeInstrumentation.mem_peak field for us.  No need to do the
    3707                 :             :                  * zero checks like we did for the serial case above.
    3708                 :             :                  */
    3709                 :           0 :                 memPeakKb = BYTES_TO_KILOBYTES(si->mem_peak);
    3710                 :             : 
    3711         [ #  # ]:           0 :                 if (es->format == EXPLAIN_FORMAT_TEXT)
    3712                 :             :                 {
    3713                 :           0 :                         ExplainIndentText(es);
    3714                 :           0 :                         appendStringInfo(es->str,
    3715                 :             :                                                          "Hits: " UINT64_FORMAT "  Misses: " UINT64_FORMAT "  Evictions: " UINT64_FORMAT "  Overflows: " UINT64_FORMAT "  Memory Usage: " INT64_FORMAT "kB\n",
    3716                 :           0 :                                                          si->cache_hits, si->cache_misses,
    3717                 :           0 :                                                          si->cache_evictions, si->cache_overflows,
    3718                 :           0 :                                                          memPeakKb);
    3719                 :           0 :                 }
    3720                 :             :                 else
    3721                 :             :                 {
    3722                 :           0 :                         ExplainPropertyInteger("Cache Hits", NULL,
    3723                 :           0 :                                                                    si->cache_hits, es);
    3724                 :           0 :                         ExplainPropertyInteger("Cache Misses", NULL,
    3725                 :           0 :                                                                    si->cache_misses, es);
    3726                 :           0 :                         ExplainPropertyInteger("Cache Evictions", NULL,
    3727                 :           0 :                                                                    si->cache_evictions, es);
    3728                 :           0 :                         ExplainPropertyInteger("Cache Overflows", NULL,
    3729                 :           0 :                                                                    si->cache_overflows, es);
    3730                 :           0 :                         ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
    3731                 :           0 :                                                                    es);
    3732                 :             :                 }
    3733                 :             : 
    3734         [ #  # ]:           0 :                 if (es->workers_state)
    3735                 :           0 :                         ExplainCloseWorker(n, es);
    3736         [ #  # ]:           0 :         }
    3737                 :          41 : }
    3738                 :             : 
    3739                 :             : /*
    3740                 :             :  * Show information on hash aggregate memory usage and batches.
    3741                 :             :  */
    3742                 :             : static void
    3743                 :        1715 : show_hashagg_info(AggState *aggstate, ExplainState *es)
    3744                 :             : {
    3745                 :        1715 :         Agg                *agg = (Agg *) aggstate->ss.ps.plan;
    3746                 :        1715 :         int64           memPeakKb = BYTES_TO_KILOBYTES(aggstate->hash_mem_peak);
    3747                 :             : 
    3748   [ +  +  +  + ]:        1715 :         if (agg->aggstrategy != AGG_HASHED &&
    3749                 :        1339 :                 agg->aggstrategy != AGG_MIXED)
    3750                 :        1321 :                 return;
    3751                 :             : 
    3752         [ -  + ]:         394 :         if (es->format != EXPLAIN_FORMAT_TEXT)
    3753                 :             :         {
    3754         [ #  # ]:           0 :                 if (es->costs)
    3755                 :           0 :                         ExplainPropertyInteger("Planned Partitions", NULL,
    3756                 :           0 :                                                                    aggstate->hash_planned_partitions, es);
    3757                 :             : 
    3758                 :             :                 /*
    3759                 :             :                  * During parallel query the leader may have not helped out.  We
    3760                 :             :                  * detect this by checking how much memory it used.  If we find it
    3761                 :             :                  * didn't do any work then we don't show its properties.
    3762                 :             :                  */
    3763   [ #  #  #  # ]:           0 :                 if (es->analyze && aggstate->hash_mem_peak > 0)
    3764                 :             :                 {
    3765                 :           0 :                         ExplainPropertyInteger("HashAgg Batches", NULL,
    3766                 :           0 :                                                                    aggstate->hash_batches_used, es);
    3767                 :           0 :                         ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
    3768                 :           0 :                         ExplainPropertyInteger("Disk Usage", "kB",
    3769                 :           0 :                                                                    aggstate->hash_disk_used, es);
    3770                 :           0 :                 }
    3771                 :           0 :         }
    3772                 :             :         else
    3773                 :             :         {
    3774                 :         394 :                 bool            gotone = false;
    3775                 :             : 
    3776   [ +  +  +  - ]:         394 :                 if (es->costs && aggstate->hash_planned_partitions > 0)
    3777                 :             :                 {
    3778                 :           0 :                         ExplainIndentText(es);
    3779                 :           0 :                         appendStringInfo(es->str, "Planned Partitions: %d",
    3780                 :           0 :                                                          aggstate->hash_planned_partitions);
    3781                 :           0 :                         gotone = true;
    3782                 :           0 :                 }
    3783                 :             : 
    3784                 :             :                 /*
    3785                 :             :                  * During parallel query the leader may have not helped out.  We
    3786                 :             :                  * detect this by checking how much memory it used.  If we find it
    3787                 :             :                  * didn't do any work then we don't show its properties.
    3788                 :             :                  */
    3789   [ +  +  -  + ]:         394 :                 if (es->analyze && aggstate->hash_mem_peak > 0)
    3790                 :             :                 {
    3791         [ -  + ]:          97 :                         if (!gotone)
    3792                 :          97 :                                 ExplainIndentText(es);
    3793                 :             :                         else
    3794                 :           0 :                                 appendStringInfoSpaces(es->str, 2);
    3795                 :             : 
    3796                 :         194 :                         appendStringInfo(es->str, "Batches: %d  Memory Usage: " INT64_FORMAT "kB",
    3797                 :          97 :                                                          aggstate->hash_batches_used, memPeakKb);
    3798                 :          97 :                         gotone = true;
    3799                 :             : 
    3800                 :             :                         /* Only display disk usage if we spilled to disk */
    3801         [ +  - ]:          97 :                         if (aggstate->hash_batches_used > 1)
    3802                 :             :                         {
    3803                 :           0 :                                 appendStringInfo(es->str, "  Disk Usage: " UINT64_FORMAT "kB",
    3804                 :           0 :                                                                  aggstate->hash_disk_used);
    3805                 :           0 :                         }
    3806                 :          97 :                 }
    3807                 :             : 
    3808         [ +  + ]:         394 :                 if (gotone)
    3809                 :          97 :                         appendStringInfoChar(es->str, '\n');
    3810                 :         394 :         }
    3811                 :             : 
    3812                 :             :         /* Display stats for each parallel worker */
    3813   [ +  +  +  - ]:         394 :         if (es->analyze && aggstate->shared_info != NULL)
    3814                 :             :         {
    3815         [ #  # ]:           0 :                 for (int n = 0; n < aggstate->shared_info->num_workers; n++)
    3816                 :             :                 {
    3817                 :           0 :                         AggregateInstrumentation *sinstrument;
    3818                 :           0 :                         uint64          hash_disk_used;
    3819                 :           0 :                         int                     hash_batches_used;
    3820                 :             : 
    3821                 :           0 :                         sinstrument = &aggstate->shared_info->sinstrument[n];
    3822                 :             :                         /* Skip workers that didn't do anything */
    3823         [ #  # ]:           0 :                         if (sinstrument->hash_mem_peak == 0)
    3824                 :           0 :                                 continue;
    3825                 :           0 :                         hash_disk_used = sinstrument->hash_disk_used;
    3826                 :           0 :                         hash_batches_used = sinstrument->hash_batches_used;
    3827                 :           0 :                         memPeakKb = BYTES_TO_KILOBYTES(sinstrument->hash_mem_peak);
    3828                 :             : 
    3829         [ #  # ]:           0 :                         if (es->workers_state)
    3830                 :           0 :                                 ExplainOpenWorker(n, es);
    3831                 :             : 
    3832         [ #  # ]:           0 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    3833                 :             :                         {
    3834                 :           0 :                                 ExplainIndentText(es);
    3835                 :             : 
    3836                 :           0 :                                 appendStringInfo(es->str, "Batches: %d  Memory Usage: " INT64_FORMAT "kB",
    3837                 :           0 :                                                                  hash_batches_used, memPeakKb);
    3838                 :             : 
    3839                 :             :                                 /* Only display disk usage if we spilled to disk */
    3840         [ #  # ]:           0 :                                 if (hash_batches_used > 1)
    3841                 :           0 :                                         appendStringInfo(es->str, "  Disk Usage: " UINT64_FORMAT "kB",
    3842                 :           0 :                                                                          hash_disk_used);
    3843                 :           0 :                                 appendStringInfoChar(es->str, '\n');
    3844                 :           0 :                         }
    3845                 :             :                         else
    3846                 :             :                         {
    3847                 :           0 :                                 ExplainPropertyInteger("HashAgg Batches", NULL,
    3848                 :           0 :                                                                            hash_batches_used, es);
    3849                 :           0 :                                 ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
    3850                 :           0 :                                                                            es);
    3851                 :           0 :                                 ExplainPropertyInteger("Disk Usage", "kB", hash_disk_used, es);
    3852                 :             :                         }
    3853                 :             : 
    3854         [ #  # ]:           0 :                         if (es->workers_state)
    3855                 :           0 :                                 ExplainCloseWorker(n, es);
    3856         [ #  # ]:           0 :                 }
    3857                 :           0 :         }
    3858                 :        1715 : }
    3859                 :             : 
    3860                 :             : /*
    3861                 :             :  * Show the total number of index searches for a
    3862                 :             :  * IndexScan/IndexOnlyScan/BitmapIndexScan node
    3863                 :             :  */
    3864                 :             : static void
    3865                 :        1644 : show_indexsearches_info(PlanState *planstate, ExplainState *es)
    3866                 :             : {
    3867                 :        1644 :         Plan       *plan = planstate->plan;
    3868                 :        1644 :         SharedIndexScanInstrumentation *SharedInfo = NULL;
    3869                 :        1644 :         uint64          nsearches = 0;
    3870                 :             : 
    3871         [ +  + ]:        1644 :         if (!es->analyze)
    3872                 :        1440 :                 return;
    3873                 :             : 
    3874                 :             :         /* Initialize counters with stats from the local process first */
    3875   [ +  -  +  + ]:         204 :         switch (nodeTag(plan))
    3876                 :             :         {
    3877                 :             :                 case T_IndexScan:
    3878                 :             :                         {
    3879                 :         104 :                                 IndexScanState *indexstate = ((IndexScanState *) planstate);
    3880                 :             : 
    3881                 :         104 :                                 nsearches = indexstate->iss_Instrument.nsearches;
    3882                 :         104 :                                 SharedInfo = indexstate->iss_SharedInfo;
    3883                 :             :                                 break;
    3884                 :         104 :                         }
    3885                 :             :                 case T_IndexOnlyScan:
    3886                 :             :                         {
    3887                 :          16 :                                 IndexOnlyScanState *indexstate = ((IndexOnlyScanState *) planstate);
    3888                 :             : 
    3889                 :          16 :                                 nsearches = indexstate->ioss_Instrument.nsearches;
    3890                 :          16 :                                 SharedInfo = indexstate->ioss_SharedInfo;
    3891                 :             :                                 break;
    3892                 :          16 :                         }
    3893                 :             :                 case T_BitmapIndexScan:
    3894                 :             :                         {
    3895                 :          84 :                                 BitmapIndexScanState *indexstate = ((BitmapIndexScanState *) planstate);
    3896                 :             : 
    3897                 :          84 :                                 nsearches = indexstate->biss_Instrument.nsearches;
    3898                 :          84 :                                 SharedInfo = indexstate->biss_SharedInfo;
    3899                 :             :                                 break;
    3900                 :          84 :                         }
    3901                 :             :                 default:
    3902                 :           0 :                         break;
    3903                 :             :         }
    3904                 :             : 
    3905                 :             :         /* Next get the sum of the counters set within each and every process */
    3906         [ +  + ]:         204 :         if (SharedInfo)
    3907                 :             :         {
    3908         [ +  + ]:          90 :                 for (int i = 0; i < SharedInfo->num_workers; ++i)
    3909                 :             :                 {
    3910                 :          45 :                         IndexScanInstrumentation *winstrument = &SharedInfo->winstrument[i];
    3911                 :             : 
    3912                 :          45 :                         nsearches += winstrument->nsearches;
    3913                 :          45 :                 }
    3914                 :          45 :         }
    3915                 :             : 
    3916                 :         204 :         ExplainPropertyUInteger("Index Searches", NULL, nsearches, es);
    3917         [ -  + ]:        1644 : }
    3918                 :             : 
    3919                 :             : /*
    3920                 :             :  * Show exact/lossy pages for a BitmapHeapScan node
    3921                 :             :  */
    3922                 :             : static void
    3923                 :         673 : show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es)
    3924                 :             : {
    3925         [ +  + ]:         673 :         if (!es->analyze)
    3926                 :         590 :                 return;
    3927                 :             : 
    3928         [ +  + ]:          83 :         if (es->format != EXPLAIN_FORMAT_TEXT)
    3929                 :             :         {
    3930                 :          10 :                 ExplainPropertyUInteger("Exact Heap Blocks", NULL,
    3931                 :          10 :                                                                 planstate->stats.exact_pages, es);
    3932                 :          10 :                 ExplainPropertyUInteger("Lossy Heap Blocks", NULL,
    3933                 :          10 :                                                                 planstate->stats.lossy_pages, es);
    3934                 :          10 :         }
    3935                 :             :         else
    3936                 :             :         {
    3937   [ +  +  -  + ]:          73 :                 if (planstate->stats.exact_pages > 0 || planstate->stats.lossy_pages > 0)
    3938                 :             :                 {
    3939                 :          47 :                         ExplainIndentText(es);
    3940                 :          47 :                         appendStringInfoString(es->str, "Heap Blocks:");
    3941         [ -  + ]:          47 :                         if (planstate->stats.exact_pages > 0)
    3942                 :          47 :                                 appendStringInfo(es->str, " exact=" UINT64_FORMAT, planstate->stats.exact_pages);
    3943         [ +  - ]:          47 :                         if (planstate->stats.lossy_pages > 0)
    3944                 :           0 :                                 appendStringInfo(es->str, " lossy=" UINT64_FORMAT, planstate->stats.lossy_pages);
    3945                 :          47 :                         appendStringInfoChar(es->str, '\n');
    3946                 :          47 :                 }
    3947                 :             :         }
    3948                 :             : 
    3949                 :             :         /* Display stats for each parallel worker */
    3950         [ +  - ]:          83 :         if (planstate->pstate != NULL)
    3951                 :             :         {
    3952         [ #  # ]:           0 :                 for (int n = 0; n < planstate->sinstrument->num_workers; n++)
    3953                 :             :                 {
    3954                 :           0 :                         BitmapHeapScanInstrumentation *si = &planstate->sinstrument->sinstrument[n];
    3955                 :             : 
    3956   [ #  #  #  # ]:           0 :                         if (si->exact_pages == 0 && si->lossy_pages == 0)
    3957                 :           0 :                                 continue;
    3958                 :             : 
    3959         [ #  # ]:           0 :                         if (es->workers_state)
    3960                 :           0 :                                 ExplainOpenWorker(n, es);
    3961                 :             : 
    3962         [ #  # ]:           0 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    3963                 :             :                         {
    3964                 :           0 :                                 ExplainIndentText(es);
    3965                 :           0 :                                 appendStringInfoString(es->str, "Heap Blocks:");
    3966         [ #  # ]:           0 :                                 if (si->exact_pages > 0)
    3967                 :           0 :                                         appendStringInfo(es->str, " exact=" UINT64_FORMAT, si->exact_pages);
    3968         [ #  # ]:           0 :                                 if (si->lossy_pages > 0)
    3969                 :           0 :                                         appendStringInfo(es->str, " lossy=" UINT64_FORMAT, si->lossy_pages);
    3970                 :           0 :                                 appendStringInfoChar(es->str, '\n');
    3971                 :           0 :                         }
    3972                 :             :                         else
    3973                 :             :                         {
    3974                 :           0 :                                 ExplainPropertyUInteger("Exact Heap Blocks", NULL,
    3975                 :           0 :                                                                                 si->exact_pages, es);
    3976                 :           0 :                                 ExplainPropertyUInteger("Lossy Heap Blocks", NULL,
    3977                 :           0 :                                                                                 si->lossy_pages, es);
    3978                 :             :                         }
    3979                 :             : 
    3980         [ #  # ]:           0 :                         if (es->workers_state)
    3981                 :           0 :                                 ExplainCloseWorker(n, es);
    3982      [ #  #  # ]:           0 :                 }
    3983                 :           0 :         }
    3984                 :         673 : }
    3985                 :             : 
    3986                 :             : /*
    3987                 :             :  * If it's EXPLAIN ANALYZE, show instrumentation information for a plan node
    3988                 :             :  *
    3989                 :             :  * "which" identifies which instrumentation counter to print
    3990                 :             :  */
    3991                 :             : static void
    3992                 :        4183 : show_instrumentation_count(const char *qlabel, int which,
    3993                 :             :                                                    PlanState *planstate, ExplainState *es)
    3994                 :             : {
    3995                 :        4183 :         double          nfiltered;
    3996                 :        4183 :         double          nloops;
    3997                 :             : 
    3998   [ +  +  -  + ]:        4183 :         if (!es->analyze || !planstate->instrument)
    3999                 :        3586 :                 return;
    4000                 :             : 
    4001         [ +  + ]:         597 :         if (which == 2)
    4002                 :         187 :                 nfiltered = planstate->instrument->nfiltered2;
    4003                 :             :         else
    4004                 :         410 :                 nfiltered = planstate->instrument->nfiltered1;
    4005                 :         597 :         nloops = planstate->instrument->nloops;
    4006                 :             : 
    4007                 :             :         /* In text mode, suppress zero counts; they're not interesting enough */
    4008   [ +  +  +  + ]:         597 :         if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
    4009                 :             :         {
    4010         [ +  - ]:         282 :                 if (nloops > 0)
    4011                 :         282 :                         ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es);
    4012                 :             :                 else
    4013                 :           0 :                         ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es);
    4014                 :         282 :         }
    4015         [ -  + ]:        4183 : }
    4016                 :             : 
    4017                 :             : /*
    4018                 :             :  * Show extra information for a ForeignScan node.
    4019                 :             :  */
    4020                 :             : static void
    4021                 :           0 : show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
    4022                 :             : {
    4023                 :           0 :         FdwRoutine *fdwroutine = fsstate->fdwroutine;
    4024                 :             : 
    4025                 :             :         /* Let the FDW emit whatever fields it wants */
    4026         [ #  # ]:           0 :         if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
    4027                 :             :         {
    4028         [ #  # ]:           0 :                 if (fdwroutine->ExplainDirectModify != NULL)
    4029                 :           0 :                         fdwroutine->ExplainDirectModify(fsstate, es);
    4030                 :           0 :         }
    4031                 :             :         else
    4032                 :             :         {
    4033         [ #  # ]:           0 :                 if (fdwroutine->ExplainForeignScan != NULL)
    4034                 :           0 :                         fdwroutine->ExplainForeignScan(fsstate, es);
    4035                 :             :         }
    4036                 :           0 : }
    4037                 :             : 
    4038                 :             : /*
    4039                 :             :  * Fetch the name of an index in an EXPLAIN
    4040                 :             :  *
    4041                 :             :  * We allow plugins to get control here so that plans involving hypothetical
    4042                 :             :  * indexes can be explained.
    4043                 :             :  *
    4044                 :             :  * Note: names returned by this function should be "raw"; the caller will
    4045                 :             :  * apply quoting if needed.  Formerly the convention was to do quoting here,
    4046                 :             :  * but we don't want that in non-text output formats.
    4047                 :             :  */
    4048                 :             : static const char *
    4049                 :        1644 : explain_get_index_name(Oid indexId)
    4050                 :             : {
    4051                 :        1644 :         const char *result;
    4052                 :             : 
    4053         [ -  + ]:        1644 :         if (explain_get_index_name_hook)
    4054                 :           0 :                 result = (*explain_get_index_name_hook) (indexId);
    4055                 :             :         else
    4056                 :        1644 :                 result = NULL;
    4057         [ -  + ]:        1644 :         if (result == NULL)
    4058                 :             :         {
    4059                 :             :                 /* default behavior: look it up in the catalogs */
    4060                 :        1644 :                 result = get_rel_name(indexId);
    4061         [ +  - ]:        1644 :                 if (result == NULL)
    4062   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for index %u", indexId);
    4063                 :        1644 :         }
    4064                 :        3288 :         return result;
    4065                 :        1644 : }
    4066                 :             : 
    4067                 :             : /*
    4068                 :             :  * Return whether show_buffer_usage would have anything to print, if given
    4069                 :             :  * the same 'usage' data.  Note that when the format is anything other than
    4070                 :             :  * text, we print even if the counters are all zeroes.
    4071                 :             :  */
    4072                 :             : static bool
    4073                 :        3612 : peek_buffer_usage(ExplainState *es, const BufferUsage *usage)
    4074                 :             : {
    4075                 :        3612 :         bool            has_shared;
    4076                 :        3612 :         bool            has_local;
    4077                 :        3612 :         bool            has_temp;
    4078                 :        3612 :         bool            has_shared_timing;
    4079                 :        3612 :         bool            has_local_timing;
    4080                 :        3612 :         bool            has_temp_timing;
    4081                 :             : 
    4082         [ +  + ]:        3612 :         if (usage == NULL)
    4083                 :        3186 :                 return false;
    4084                 :             : 
    4085         [ +  + ]:         426 :         if (es->format != EXPLAIN_FORMAT_TEXT)
    4086                 :          34 :                 return true;
    4087                 :             : 
    4088         [ +  + ]:         690 :         has_shared = (usage->shared_blks_hit > 0 ||
    4089         [ +  - ]:         298 :                                   usage->shared_blks_read > 0 ||
    4090         [ -  + ]:         298 :                                   usage->shared_blks_dirtied > 0 ||
    4091                 :         298 :                                   usage->shared_blks_written > 0);
    4092         [ +  - ]:         784 :         has_local = (usage->local_blks_hit > 0 ||
    4093         [ +  - ]:         392 :                                  usage->local_blks_read > 0 ||
    4094         [ -  + ]:         392 :                                  usage->local_blks_dirtied > 0 ||
    4095                 :         392 :                                  usage->local_blks_written > 0);
    4096         [ -  + ]:         392 :         has_temp = (usage->temp_blks_read > 0 ||
    4097                 :         392 :                                 usage->temp_blks_written > 0);
    4098         [ -  + ]:         392 :         has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
    4099                 :         392 :                                                  !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
    4100         [ -  + ]:         392 :         has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
    4101                 :         392 :                                                 !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
    4102         [ -  + ]:         392 :         has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
    4103                 :         392 :                                            !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
    4104                 :             : 
    4105   [ +  +  +  -  :         690 :         return has_shared || has_local || has_temp || has_shared_timing ||
             +  -  +  - ]
    4106         [ -  + ]:         298 :                 has_local_timing || has_temp_timing;
    4107                 :        3612 : }
    4108                 :             : 
    4109                 :             : /*
    4110                 :             :  * Show buffer usage details.  This better be sync with peek_buffer_usage.
    4111                 :             :  */
    4112                 :             : static void
    4113                 :         832 : show_buffer_usage(ExplainState *es, const BufferUsage *usage)
    4114                 :             : {
    4115         [ +  + ]:         832 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    4116                 :             :         {
    4117         [ +  + ]:         647 :                 bool            has_shared = (usage->shared_blks_hit > 0 ||
    4118         [ +  - ]:           6 :                                                                   usage->shared_blks_read > 0 ||
    4119         [ -  + ]:           6 :                                                                   usage->shared_blks_dirtied > 0 ||
    4120                 :           6 :                                                                   usage->shared_blks_written > 0);
    4121         [ +  - ]:        1282 :                 bool            has_local = (usage->local_blks_hit > 0 ||
    4122         [ +  - ]:         641 :                                                                  usage->local_blks_read > 0 ||
    4123         [ -  + ]:         641 :                                                                  usage->local_blks_dirtied > 0 ||
    4124                 :         641 :                                                                  usage->local_blks_written > 0);
    4125         [ -  + ]:         641 :                 bool            has_temp = (usage->temp_blks_read > 0 ||
    4126                 :         641 :                                                                 usage->temp_blks_written > 0);
    4127         [ -  + ]:         641 :                 bool            has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
    4128                 :         641 :                                                                                  !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
    4129         [ -  + ]:         641 :                 bool            has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
    4130                 :         641 :                                                                                 !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
    4131         [ -  + ]:         641 :                 bool            has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
    4132                 :         641 :                                                                            !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
    4133                 :             : 
    4134                 :             :                 /* Show only positive counter values. */
    4135   [ +  +  +  -  :         641 :                 if (has_shared || has_local || has_temp)
                   -  + ]
    4136                 :             :                 {
    4137                 :         635 :                         ExplainIndentText(es);
    4138                 :         635 :                         appendStringInfoString(es->str, "Buffers:");
    4139                 :             : 
    4140         [ -  + ]:         635 :                         if (has_shared)
    4141                 :             :                         {
    4142                 :         635 :                                 appendStringInfoString(es->str, " shared");
    4143         [ -  + ]:         635 :                                 if (usage->shared_blks_hit > 0)
    4144                 :        1270 :                                         appendStringInfo(es->str, " hit=%" PRId64,
    4145                 :         635 :                                                                          usage->shared_blks_hit);
    4146         [ +  - ]:         635 :                                 if (usage->shared_blks_read > 0)
    4147                 :           0 :                                         appendStringInfo(es->str, " read=%" PRId64,
    4148                 :           0 :                                                                          usage->shared_blks_read);
    4149         [ +  - ]:         635 :                                 if (usage->shared_blks_dirtied > 0)
    4150                 :           0 :                                         appendStringInfo(es->str, " dirtied=%" PRId64,
    4151                 :           0 :                                                                          usage->shared_blks_dirtied);
    4152         [ +  - ]:         635 :                                 if (usage->shared_blks_written > 0)
    4153                 :           0 :                                         appendStringInfo(es->str, " written=%" PRId64,
    4154                 :           0 :                                                                          usage->shared_blks_written);
    4155   [ +  -  -  + ]:         635 :                                 if (has_local || has_temp)
    4156                 :           0 :                                         appendStringInfoChar(es->str, ',');
    4157                 :         635 :                         }
    4158         [ +  - ]:         635 :                         if (has_local)
    4159                 :             :                         {
    4160                 :           0 :                                 appendStringInfoString(es->str, " local");
    4161         [ #  # ]:           0 :                                 if (usage->local_blks_hit > 0)
    4162                 :           0 :                                         appendStringInfo(es->str, " hit=%" PRId64,
    4163                 :           0 :                                                                          usage->local_blks_hit);
    4164         [ #  # ]:           0 :                                 if (usage->local_blks_read > 0)
    4165                 :           0 :                                         appendStringInfo(es->str, " read=%" PRId64,
    4166                 :           0 :                                                                          usage->local_blks_read);
    4167         [ #  # ]:           0 :                                 if (usage->local_blks_dirtied > 0)
    4168                 :           0 :                                         appendStringInfo(es->str, " dirtied=%" PRId64,
    4169                 :           0 :                                                                          usage->local_blks_dirtied);
    4170         [ #  # ]:           0 :                                 if (usage->local_blks_written > 0)
    4171                 :           0 :                                         appendStringInfo(es->str, " written=%" PRId64,
    4172                 :           0 :                                                                          usage->local_blks_written);
    4173         [ #  # ]:           0 :                                 if (has_temp)
    4174                 :           0 :                                         appendStringInfoChar(es->str, ',');
    4175                 :           0 :                         }
    4176         [ +  - ]:         635 :                         if (has_temp)
    4177                 :             :                         {
    4178                 :           0 :                                 appendStringInfoString(es->str, " temp");
    4179         [ #  # ]:           0 :                                 if (usage->temp_blks_read > 0)
    4180                 :           0 :                                         appendStringInfo(es->str, " read=%" PRId64,
    4181                 :           0 :                                                                          usage->temp_blks_read);
    4182         [ #  # ]:           0 :                                 if (usage->temp_blks_written > 0)
    4183                 :           0 :                                         appendStringInfo(es->str, " written=%" PRId64,
    4184                 :           0 :                                                                          usage->temp_blks_written);
    4185                 :           0 :                         }
    4186                 :         635 :                         appendStringInfoChar(es->str, '\n');
    4187                 :         635 :                 }
    4188                 :             : 
    4189                 :             :                 /* As above, show only positive counter values. */
    4190   [ +  -  +  -  :         641 :                 if (has_shared_timing || has_local_timing || has_temp_timing)
                   -  + ]
    4191                 :             :                 {
    4192                 :           0 :                         ExplainIndentText(es);
    4193                 :           0 :                         appendStringInfoString(es->str, "I/O Timings:");
    4194                 :             : 
    4195         [ #  # ]:           0 :                         if (has_shared_timing)
    4196                 :             :                         {
    4197                 :           0 :                                 appendStringInfoString(es->str, " shared");
    4198         [ #  # ]:           0 :                                 if (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time))
    4199                 :           0 :                                         appendStringInfo(es->str, " read=%0.3f",
    4200                 :           0 :                                                                          INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time));
    4201         [ #  # ]:           0 :                                 if (!INSTR_TIME_IS_ZERO(usage->shared_blk_write_time))
    4202                 :           0 :                                         appendStringInfo(es->str, " write=%0.3f",
    4203                 :           0 :                                                                          INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time));
    4204   [ #  #  #  # ]:           0 :                                 if (has_local_timing || has_temp_timing)
    4205                 :           0 :                                         appendStringInfoChar(es->str, ',');
    4206                 :           0 :                         }
    4207         [ #  # ]:           0 :                         if (has_local_timing)
    4208                 :             :                         {
    4209                 :           0 :                                 appendStringInfoString(es->str, " local");
    4210         [ #  # ]:           0 :                                 if (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time))
    4211                 :           0 :                                         appendStringInfo(es->str, " read=%0.3f",
    4212                 :           0 :                                                                          INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time));
    4213         [ #  # ]:           0 :                                 if (!INSTR_TIME_IS_ZERO(usage->local_blk_write_time))
    4214                 :           0 :                                         appendStringInfo(es->str, " write=%0.3f",
    4215                 :           0 :                                                                          INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time));
    4216         [ #  # ]:           0 :                                 if (has_temp_timing)
    4217                 :           0 :                                         appendStringInfoChar(es->str, ',');
    4218                 :           0 :                         }
    4219         [ #  # ]:           0 :                         if (has_temp_timing)
    4220                 :             :                         {
    4221                 :           0 :                                 appendStringInfoString(es->str, " temp");
    4222         [ #  # ]:           0 :                                 if (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time))
    4223                 :           0 :                                         appendStringInfo(es->str, " read=%0.3f",
    4224                 :           0 :                                                                          INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time));
    4225         [ #  # ]:           0 :                                 if (!INSTR_TIME_IS_ZERO(usage->temp_blk_write_time))
    4226                 :           0 :                                         appendStringInfo(es->str, " write=%0.3f",
    4227                 :           0 :                                                                          INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time));
    4228                 :           0 :                         }
    4229                 :           0 :                         appendStringInfoChar(es->str, '\n');
    4230                 :           0 :                 }
    4231                 :         641 :         }
    4232                 :             :         else
    4233                 :             :         {
    4234                 :         191 :                 ExplainPropertyInteger("Shared Hit Blocks", NULL,
    4235                 :         191 :                                                            usage->shared_blks_hit, es);
    4236                 :         191 :                 ExplainPropertyInteger("Shared Read Blocks", NULL,
    4237                 :         191 :                                                            usage->shared_blks_read, es);
    4238                 :         191 :                 ExplainPropertyInteger("Shared Dirtied Blocks", NULL,
    4239                 :         191 :                                                            usage->shared_blks_dirtied, es);
    4240                 :         191 :                 ExplainPropertyInteger("Shared Written Blocks", NULL,
    4241                 :         191 :                                                            usage->shared_blks_written, es);
    4242                 :         191 :                 ExplainPropertyInteger("Local Hit Blocks", NULL,
    4243                 :         191 :                                                            usage->local_blks_hit, es);
    4244                 :         191 :                 ExplainPropertyInteger("Local Read Blocks", NULL,
    4245                 :         191 :                                                            usage->local_blks_read, es);
    4246                 :         191 :                 ExplainPropertyInteger("Local Dirtied Blocks", NULL,
    4247                 :         191 :                                                            usage->local_blks_dirtied, es);
    4248                 :         191 :                 ExplainPropertyInteger("Local Written Blocks", NULL,
    4249                 :         191 :                                                            usage->local_blks_written, es);
    4250                 :         191 :                 ExplainPropertyInteger("Temp Read Blocks", NULL,
    4251                 :         191 :                                                            usage->temp_blks_read, es);
    4252                 :         191 :                 ExplainPropertyInteger("Temp Written Blocks", NULL,
    4253                 :         191 :                                                            usage->temp_blks_written, es);
    4254         [ +  + ]:         191 :                 if (track_io_timing)
    4255                 :             :                 {
    4256                 :           2 :                         ExplainPropertyFloat("Shared I/O Read Time", "ms",
    4257                 :           2 :                                                                  INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time),
    4258                 :           2 :                                                                  3, es);
    4259                 :           2 :                         ExplainPropertyFloat("Shared I/O Write Time", "ms",
    4260                 :           2 :                                                                  INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time),
    4261                 :           2 :                                                                  3, es);
    4262                 :           2 :                         ExplainPropertyFloat("Local I/O Read Time", "ms",
    4263                 :           2 :                                                                  INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time),
    4264                 :           2 :                                                                  3, es);
    4265                 :           2 :                         ExplainPropertyFloat("Local I/O Write Time", "ms",
    4266                 :           2 :                                                                  INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time),
    4267                 :           2 :                                                                  3, es);
    4268                 :           2 :                         ExplainPropertyFloat("Temp I/O Read Time", "ms",
    4269                 :           2 :                                                                  INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time),
    4270                 :           2 :                                                                  3, es);
    4271                 :           2 :                         ExplainPropertyFloat("Temp I/O Write Time", "ms",
    4272                 :           2 :                                                                  INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time),
    4273                 :           2 :                                                                  3, es);
    4274                 :           2 :                 }
    4275                 :             :         }
    4276                 :         832 : }
    4277                 :             : 
    4278                 :             : /*
    4279                 :             :  * Show WAL usage details.
    4280                 :             :  */
    4281                 :             : static void
    4282                 :           0 : show_wal_usage(ExplainState *es, const WalUsage *usage)
    4283                 :             : {
    4284         [ #  # ]:           0 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    4285                 :             :         {
    4286                 :             :                 /* Show only positive counter values. */
    4287   [ #  #  #  # ]:           0 :                 if ((usage->wal_records > 0) || (usage->wal_fpi > 0) ||
    4288   [ #  #  #  #  :           0 :                         (usage->wal_bytes > 0) || (usage->wal_buffers_full > 0) ||
                   #  # ]
    4289                 :           0 :                         (usage->wal_fpi_bytes > 0))
    4290                 :             :                 {
    4291                 :           0 :                         ExplainIndentText(es);
    4292                 :           0 :                         appendStringInfoString(es->str, "WAL:");
    4293                 :             : 
    4294         [ #  # ]:           0 :                         if (usage->wal_records > 0)
    4295                 :           0 :                                 appendStringInfo(es->str, " records=%" PRId64,
    4296                 :           0 :                                                                  usage->wal_records);
    4297         [ #  # ]:           0 :                         if (usage->wal_fpi > 0)
    4298                 :           0 :                                 appendStringInfo(es->str, " fpi=%" PRId64,
    4299                 :           0 :                                                                  usage->wal_fpi);
    4300         [ #  # ]:           0 :                         if (usage->wal_bytes > 0)
    4301                 :           0 :                                 appendStringInfo(es->str, " bytes=%" PRIu64,
    4302                 :           0 :                                                                  usage->wal_bytes);
    4303         [ #  # ]:           0 :                         if (usage->wal_fpi_bytes > 0)
    4304                 :           0 :                                 appendStringInfo(es->str, " fpi bytes=%" PRIu64,
    4305                 :           0 :                                                                  usage->wal_fpi_bytes);
    4306         [ #  # ]:           0 :                         if (usage->wal_buffers_full > 0)
    4307                 :           0 :                                 appendStringInfo(es->str, " buffers full=%" PRId64,
    4308                 :           0 :                                                                  usage->wal_buffers_full);
    4309                 :           0 :                         appendStringInfoChar(es->str, '\n');
    4310                 :           0 :                 }
    4311                 :           0 :         }
    4312                 :             :         else
    4313                 :             :         {
    4314                 :           0 :                 ExplainPropertyInteger("WAL Records", NULL,
    4315                 :           0 :                                                            usage->wal_records, es);
    4316                 :           0 :                 ExplainPropertyInteger("WAL FPI", NULL,
    4317                 :           0 :                                                            usage->wal_fpi, es);
    4318                 :           0 :                 ExplainPropertyUInteger("WAL Bytes", NULL,
    4319                 :           0 :                                                                 usage->wal_bytes, es);
    4320                 :           0 :                 ExplainPropertyUInteger("WAL FPI Bytes", NULL,
    4321                 :           0 :                                                                 usage->wal_fpi_bytes, es);
    4322                 :           0 :                 ExplainPropertyInteger("WAL Buffers Full", NULL,
    4323                 :           0 :                                                            usage->wal_buffers_full, es);
    4324                 :             :         }
    4325                 :           0 : }
    4326                 :             : 
    4327                 :             : /*
    4328                 :             :  * Show memory usage details.
    4329                 :             :  */
    4330                 :             : static void
    4331                 :           5 : show_memory_counters(ExplainState *es, const MemoryContextCounters *mem_counters)
    4332                 :             : {
    4333                 :           5 :         int64           memUsedkB = BYTES_TO_KILOBYTES(mem_counters->totalspace -
    4334                 :             :                                                                                            mem_counters->freespace);
    4335                 :           5 :         int64           memAllocatedkB = BYTES_TO_KILOBYTES(mem_counters->totalspace);
    4336                 :             : 
    4337         [ +  + ]:           5 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    4338                 :             :         {
    4339                 :           3 :                 ExplainIndentText(es);
    4340                 :           6 :                 appendStringInfo(es->str,
    4341                 :             :                                                  "Memory: used=" INT64_FORMAT "kB  allocated=" INT64_FORMAT "kB",
    4342                 :           3 :                                                  memUsedkB, memAllocatedkB);
    4343                 :           3 :                 appendStringInfoChar(es->str, '\n');
    4344                 :           3 :         }
    4345                 :             :         else
    4346                 :             :         {
    4347                 :           2 :                 ExplainPropertyInteger("Memory Used", "kB", memUsedkB, es);
    4348                 :           2 :                 ExplainPropertyInteger("Memory Allocated", "kB", memAllocatedkB, es);
    4349                 :             :         }
    4350                 :           5 : }
    4351                 :             : 
    4352                 :             : 
    4353                 :             : /*
    4354                 :             :  * Add some additional details about an IndexScan or IndexOnlyScan
    4355                 :             :  */
    4356                 :             : static void
    4357                 :         939 : ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
    4358                 :             :                                                 ExplainState *es)
    4359                 :             : {
    4360                 :         939 :         const char *indexname = explain_get_index_name(indexid);
    4361                 :             : 
    4362         [ -  + ]:         939 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    4363                 :             :         {
    4364         [ +  + ]:         939 :                 if (ScanDirectionIsBackward(indexorderdir))
    4365                 :          44 :                         appendStringInfoString(es->str, " Backward");
    4366                 :         939 :                 appendStringInfo(es->str, " using %s", quote_identifier(indexname));
    4367                 :         939 :         }
    4368                 :             :         else
    4369                 :             :         {
    4370                 :           0 :                 const char *scandir;
    4371                 :             : 
    4372      [ #  #  # ]:           0 :                 switch (indexorderdir)
    4373                 :             :                 {
    4374                 :             :                         case BackwardScanDirection:
    4375                 :           0 :                                 scandir = "Backward";
    4376                 :           0 :                                 break;
    4377                 :             :                         case ForwardScanDirection:
    4378                 :           0 :                                 scandir = "Forward";
    4379                 :           0 :                                 break;
    4380                 :             :                         default:
    4381                 :           0 :                                 scandir = "???";
    4382                 :           0 :                                 break;
    4383                 :             :                 }
    4384                 :           0 :                 ExplainPropertyText("Scan Direction", scandir, es);
    4385                 :           0 :                 ExplainPropertyText("Index Name", indexname, es);
    4386                 :           0 :         }
    4387                 :         939 : }
    4388                 :             : 
    4389                 :             : /*
    4390                 :             :  * Show the target of a Scan node
    4391                 :             :  */
    4392                 :             : static void
    4393                 :        6245 : ExplainScanTarget(Scan *plan, ExplainState *es)
    4394                 :             : {
    4395                 :        6245 :         ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
    4396                 :        6245 : }
    4397                 :             : 
    4398                 :             : /*
    4399                 :             :  * Show the target of a ModifyTable node
    4400                 :             :  *
    4401                 :             :  * Here we show the nominal target (ie, the relation that was named in the
    4402                 :             :  * original query).  If the actual target(s) is/are different, we'll show them
    4403                 :             :  * in show_modifytable_info().
    4404                 :             :  */
    4405                 :             : static void
    4406                 :         142 : ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
    4407                 :             : {
    4408                 :         142 :         ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
    4409                 :         142 : }
    4410                 :             : 
    4411                 :             : /*
    4412                 :             :  * Show the target relation of a scan or modify node
    4413                 :             :  */
    4414                 :             : static void
    4415                 :        6463 : ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
    4416                 :             : {
    4417                 :        6463 :         char       *objectname = NULL;
    4418                 :        6463 :         char       *namespace = NULL;
    4419                 :        6463 :         const char *objecttag = NULL;
    4420                 :        6463 :         RangeTblEntry *rte;
    4421                 :        6463 :         char       *refname;
    4422                 :             : 
    4423                 :        6463 :         rte = rt_fetch(rti, es->rtable);
    4424                 :        6463 :         refname = (char *) list_nth(es->rtable_names, rti - 1);
    4425         [ +  - ]:        6463 :         if (refname == NULL)
    4426                 :           0 :                 refname = rte->eref->aliasname;
    4427                 :             : 
    4428   [ +  +  +  +  :        6463 :         switch (nodeTag(plan))
             +  +  -  + ]
    4429                 :             :         {
    4430                 :             :                 case T_SeqScan:
    4431                 :             :                 case T_SampleScan:
    4432                 :             :                 case T_IndexScan:
    4433                 :             :                 case T_IndexOnlyScan:
    4434                 :             :                 case T_BitmapHeapScan:
    4435                 :             :                 case T_TidScan:
    4436                 :             :                 case T_TidRangeScan:
    4437                 :             :                 case T_ForeignScan:
    4438                 :             :                 case T_CustomScan:
    4439                 :             :                 case T_ModifyTable:
    4440                 :             :                         /* Assert it's on a real relation */
    4441         [ +  - ]:        6120 :                         Assert(rte->rtekind == RTE_RELATION);
    4442                 :        6120 :                         objectname = get_rel_name(rte->relid);
    4443         [ +  + ]:        6120 :                         if (es->verbose)
    4444                 :         576 :                                 namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid));
    4445                 :        6120 :                         objecttag = "Relation Name";
    4446                 :        6120 :                         break;
    4447                 :             :                 case T_FunctionScan:
    4448                 :             :                         {
    4449                 :         109 :                                 FunctionScan *fscan = (FunctionScan *) plan;
    4450                 :             : 
    4451                 :             :                                 /* Assert it's on a RangeFunction */
    4452         [ +  - ]:         109 :                                 Assert(rte->rtekind == RTE_FUNCTION);
    4453                 :             : 
    4454                 :             :                                 /*
    4455                 :             :                                  * If the expression is still a function call of a single
    4456                 :             :                                  * function, we can get the real name of the function.
    4457                 :             :                                  * Otherwise, punt.  (Even if it was a single function call
    4458                 :             :                                  * originally, the optimizer could have simplified it away.)
    4459                 :             :                                  */
    4460         [ -  + ]:         109 :                                 if (list_length(fscan->functions) == 1)
    4461                 :             :                                 {
    4462                 :         109 :                                         RangeTblFunction *rtfunc = (RangeTblFunction *) linitial(fscan->functions);
    4463                 :             : 
    4464         [ +  + ]:         109 :                                         if (IsA(rtfunc->funcexpr, FuncExpr))
    4465                 :             :                                         {
    4466                 :         105 :                                                 FuncExpr   *funcexpr = (FuncExpr *) rtfunc->funcexpr;
    4467                 :         105 :                                                 Oid                     funcid = funcexpr->funcid;
    4468                 :             : 
    4469                 :         105 :                                                 objectname = get_func_name(funcid);
    4470         [ +  + ]:         105 :                                                 if (es->verbose)
    4471                 :          29 :                                                         namespace = get_namespace_name_or_temp(get_func_namespace(funcid));
    4472                 :         105 :                                         }
    4473                 :         109 :                                 }
    4474                 :         109 :                                 objecttag = "Function Name";
    4475                 :         109 :                         }
    4476                 :         109 :                         break;
    4477                 :             :                 case T_TableFuncScan:
    4478                 :             :                         {
    4479                 :          13 :                                 TableFunc  *tablefunc = ((TableFuncScan *) plan)->tablefunc;
    4480                 :             : 
    4481         [ +  - ]:          13 :                                 Assert(rte->rtekind == RTE_TABLEFUNC);
    4482      [ +  +  - ]:          13 :                                 switch (tablefunc->functype)
    4483                 :             :                                 {
    4484                 :             :                                         case TFT_XMLTABLE:
    4485                 :           6 :                                                 objectname = "xmltable";
    4486                 :           6 :                                                 break;
    4487                 :             :                                         case TFT_JSON_TABLE:
    4488                 :           7 :                                                 objectname = "json_table";
    4489                 :           7 :                                                 break;
    4490                 :             :                                         default:
    4491   [ #  #  #  # ]:           0 :                                                 elog(ERROR, "invalid TableFunc type %d",
    4492                 :             :                                                          (int) tablefunc->functype);
    4493                 :           0 :                                 }
    4494                 :          13 :                                 objecttag = "Table Function Name";
    4495                 :          13 :                         }
    4496                 :          13 :                         break;
    4497                 :             :                 case T_ValuesScan:
    4498         [ +  - ]:          98 :                         Assert(rte->rtekind == RTE_VALUES);
    4499                 :          98 :                         break;
    4500                 :             :                 case T_CteScan:
    4501                 :             :                         /* Assert it's on a non-self-reference CTE */
    4502         [ +  - ]:          41 :                         Assert(rte->rtekind == RTE_CTE);
    4503         [ +  - ]:          41 :                         Assert(!rte->self_reference);
    4504                 :          41 :                         objectname = rte->ctename;
    4505                 :          41 :                         objecttag = "CTE Name";
    4506                 :          41 :                         break;
    4507                 :             :                 case T_NamedTuplestoreScan:
    4508         [ #  # ]:           0 :                         Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
    4509                 :           0 :                         objectname = rte->enrname;
    4510                 :           0 :                         objecttag = "Tuplestore Name";
    4511                 :           0 :                         break;
    4512                 :             :                 case T_WorkTableScan:
    4513                 :             :                         /* Assert it's on a self-reference CTE */
    4514         [ +  - ]:           9 :                         Assert(rte->rtekind == RTE_CTE);
    4515         [ +  - ]:           9 :                         Assert(rte->self_reference);
    4516                 :           9 :                         objectname = rte->ctename;
    4517                 :           9 :                         objecttag = "CTE Name";
    4518                 :           9 :                         break;
    4519                 :             :                 default:
    4520                 :          73 :                         break;
    4521                 :             :         }
    4522                 :             : 
    4523         [ +  + ]:        6463 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    4524                 :             :         {
    4525                 :        6397 :                 appendStringInfoString(es->str, " on");
    4526         [ +  + ]:        6397 :                 if (namespace != NULL)
    4527                 :        1206 :                         appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
    4528                 :         603 :                                                          quote_identifier(objectname));
    4529         [ +  + ]:        5794 :                 else if (objectname != NULL)
    4530                 :        5619 :                         appendStringInfo(es->str, " %s", quote_identifier(objectname));
    4531   [ +  +  +  + ]:        6397 :                 if (objectname == NULL || strcmp(refname, objectname) != 0)
    4532                 :        3826 :                         appendStringInfo(es->str, " %s", quote_identifier(refname));
    4533                 :        6397 :         }
    4534                 :             :         else
    4535                 :             :         {
    4536   [ +  -  -  + ]:          66 :                 if (objecttag != NULL && objectname != NULL)
    4537                 :          66 :                         ExplainPropertyText(objecttag, objectname, es);
    4538         [ +  + ]:          66 :                 if (namespace != NULL)
    4539                 :           2 :                         ExplainPropertyText("Schema", namespace, es);
    4540                 :          66 :                 ExplainPropertyText("Alias", refname, es);
    4541                 :             :         }
    4542                 :        6463 : }
    4543                 :             : 
    4544                 :             : /*
    4545                 :             :  * Show extra information for a ModifyTable node
    4546                 :             :  *
    4547                 :             :  * We have three objectives here.  First, if there's more than one target
    4548                 :             :  * table or it's different from the nominal target, identify the actual
    4549                 :             :  * target(s).  Second, give FDWs a chance to display extra info about foreign
    4550                 :             :  * targets.  Third, show information about ON CONFLICT.
    4551                 :             :  */
    4552                 :             : static void
    4553                 :         142 : show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
    4554                 :             :                                           ExplainState *es)
    4555                 :             : {
    4556                 :         142 :         ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
    4557                 :         142 :         const char *operation;
    4558                 :         142 :         const char *foperation;
    4559                 :         142 :         bool            labeltargets;
    4560                 :         142 :         int                     j;
    4561                 :         142 :         List       *idxNames = NIL;
    4562                 :         142 :         ListCell   *lst;
    4563                 :             : 
    4564   [ -  +  +  +  :         142 :         switch (node->operation)
                      + ]
    4565                 :             :         {
    4566                 :             :                 case CMD_INSERT:
    4567                 :          33 :                         operation = "Insert";
    4568                 :          33 :                         foperation = "Foreign Insert";
    4569                 :          33 :                         break;
    4570                 :             :                 case CMD_UPDATE:
    4571                 :          58 :                         operation = "Update";
    4572                 :          58 :                         foperation = "Foreign Update";
    4573                 :          58 :                         break;
    4574                 :             :                 case CMD_DELETE:
    4575                 :          21 :                         operation = "Delete";
    4576                 :          21 :                         foperation = "Foreign Delete";
    4577                 :          21 :                         break;
    4578                 :             :                 case CMD_MERGE:
    4579                 :          30 :                         operation = "Merge";
    4580                 :             :                         /* XXX unsupported for now, but avoid compiler noise */
    4581                 :          30 :                         foperation = "Foreign Merge";
    4582                 :          30 :                         break;
    4583                 :             :                 default:
    4584                 :           0 :                         operation = "???";
    4585                 :           0 :                         foperation = "Foreign ???";
    4586                 :           0 :                         break;
    4587                 :             :         }
    4588                 :             : 
    4589                 :             :         /*
    4590                 :             :          * Should we explicitly label target relations?
    4591                 :             :          *
    4592                 :             :          * If there's only one target relation, do not list it if it's the
    4593                 :             :          * relation named in the query, or if it has been pruned.  (Normally
    4594                 :             :          * mtstate->resultRelInfo doesn't include pruned relations, but a single
    4595                 :             :          * pruned target relation may be present, if all other target relations
    4596                 :             :          * have been pruned.  See ExecInitModifyTable().)
    4597                 :             :          */
    4598         [ +  + ]:         264 :         labeltargets = (mtstate->mt_nrels > 1 ||
    4599         [ +  - ]:         122 :                                         (mtstate->mt_nrels == 1 &&
    4600         [ +  + ]:         122 :                                          mtstate->resultRelInfo[0].ri_RangeTableIndex != node->nominalRelation &&
    4601                 :          40 :                                          bms_is_member(mtstate->resultRelInfo[0].ri_RangeTableIndex,
    4602                 :          20 :                                                                    mtstate->ps.state->es_unpruned_relids)));
    4603                 :             : 
    4604         [ +  + ]:         142 :         if (labeltargets)
    4605                 :          36 :                 ExplainOpenGroup("Target Tables", "Target Tables", false, es);
    4606                 :             : 
    4607         [ +  + ]:         324 :         for (j = 0; j < mtstate->mt_nrels; j++)
    4608                 :             :         {
    4609                 :         182 :                 ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
    4610                 :         182 :                 FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
    4611                 :             : 
    4612         [ +  + ]:         182 :                 if (labeltargets)
    4613                 :             :                 {
    4614                 :             :                         /* Open a group for this target */
    4615                 :          76 :                         ExplainOpenGroup("Target Table", NULL, true, es);
    4616                 :             : 
    4617                 :             :                         /*
    4618                 :             :                          * In text mode, decorate each target with operation type, so that
    4619                 :             :                          * ExplainTargetRel's output of " on foo" will read nicely.
    4620                 :             :                          */
    4621         [ -  + ]:          76 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    4622                 :             :                         {
    4623                 :          76 :                                 ExplainIndentText(es);
    4624                 :         152 :                                 appendStringInfoString(es->str,
    4625         [ -  + ]:          76 :                                                                            fdwroutine ? foperation : operation);
    4626                 :          76 :                         }
    4627                 :             : 
    4628                 :             :                         /* Identify target */
    4629                 :         152 :                         ExplainTargetRel((Plan *) node,
    4630                 :          76 :                                                          resultRelInfo->ri_RangeTableIndex,
    4631                 :          76 :                                                          es);
    4632                 :             : 
    4633         [ -  + ]:          76 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    4634                 :             :                         {
    4635                 :          76 :                                 appendStringInfoChar(es->str, '\n');
    4636                 :          76 :                                 es->indent++;
    4637                 :          76 :                         }
    4638                 :          76 :                 }
    4639                 :             : 
    4640                 :             :                 /* Give FDW a chance if needed */
    4641         [ +  - ]:         182 :                 if (!resultRelInfo->ri_usesFdwDirectModify &&
    4642   [ -  +  #  # ]:         182 :                         fdwroutine != NULL &&
    4643                 :           0 :                         fdwroutine->ExplainForeignModify != NULL)
    4644                 :             :                 {
    4645                 :           0 :                         List       *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
    4646                 :             : 
    4647                 :           0 :                         fdwroutine->ExplainForeignModify(mtstate,
    4648                 :           0 :                                                                                          resultRelInfo,
    4649                 :           0 :                                                                                          fdw_private,
    4650                 :           0 :                                                                                          j,
    4651                 :           0 :                                                                                          es);
    4652                 :           0 :                 }
    4653                 :             : 
    4654         [ +  + ]:         182 :                 if (labeltargets)
    4655                 :             :                 {
    4656                 :             :                         /* Undo the indentation we added in text format */
    4657         [ -  + ]:          76 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    4658                 :          76 :                                 es->indent--;
    4659                 :             : 
    4660                 :             :                         /* Close the group */
    4661                 :          76 :                         ExplainCloseGroup("Target Table", NULL, true, es);
    4662                 :          76 :                 }
    4663                 :         182 :         }
    4664                 :             : 
    4665                 :             :         /* Gather names of ON CONFLICT arbiter indexes */
    4666   [ +  +  +  +  :         175 :         foreach(lst, node->arbiterIndexes)
                   +  + ]
    4667                 :             :         {
    4668                 :          33 :                 char       *indexname = get_rel_name(lfirst_oid(lst));
    4669                 :             : 
    4670                 :          33 :                 idxNames = lappend(idxNames, indexname);
    4671                 :          33 :         }
    4672                 :             : 
    4673         [ +  + ]:         142 :         if (node->onConflictAction != ONCONFLICT_NONE)
    4674                 :             :         {
    4675                 :          24 :                 ExplainPropertyText("Conflict Resolution",
    4676                 :          24 :                                                         node->onConflictAction == ONCONFLICT_NOTHING ?
    4677                 :             :                                                         "NOTHING" : "UPDATE",
    4678                 :          24 :                                                         es);
    4679                 :             : 
    4680                 :             :                 /*
    4681                 :             :                  * Don't display arbiter indexes at all when DO NOTHING variant
    4682                 :             :                  * implicitly ignores all conflicts
    4683                 :             :                  */
    4684         [ -  + ]:          24 :                 if (idxNames)
    4685                 :          24 :                         ExplainPropertyList("Conflict Arbiter Indexes", idxNames, es);
    4686                 :             : 
    4687                 :             :                 /* ON CONFLICT DO UPDATE WHERE qual is specially displayed */
    4688         [ +  + ]:          24 :                 if (node->onConflictWhere)
    4689                 :             :                 {
    4690                 :          18 :                         show_upper_qual((List *) node->onConflictWhere, "Conflict Filter",
    4691                 :           9 :                                                         &mtstate->ps, ancestors, es);
    4692                 :           9 :                         show_instrumentation_count("Rows Removed by Conflict Filter", 1, &mtstate->ps, es);
    4693                 :           9 :                 }
    4694                 :             : 
    4695                 :             :                 /* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
    4696   [ -  +  #  # ]:          24 :                 if (es->analyze && mtstate->ps.instrument)
    4697                 :             :                 {
    4698                 :           0 :                         double          total;
    4699                 :           0 :                         double          insert_path;
    4700                 :           0 :                         double          other_path;
    4701                 :             : 
    4702                 :           0 :                         InstrEndLoop(outerPlanState(mtstate)->instrument);
    4703                 :             : 
    4704                 :             :                         /* count the number of source rows */
    4705                 :           0 :                         total = outerPlanState(mtstate)->instrument->ntuples;
    4706                 :           0 :                         other_path = mtstate->ps.instrument->ntuples2;
    4707                 :           0 :                         insert_path = total - other_path;
    4708                 :             : 
    4709                 :           0 :                         ExplainPropertyFloat("Tuples Inserted", NULL,
    4710                 :           0 :                                                                  insert_path, 0, es);
    4711                 :           0 :                         ExplainPropertyFloat("Conflicting Tuples", NULL,
    4712                 :           0 :                                                                  other_path, 0, es);
    4713                 :           0 :                 }
    4714                 :          24 :         }
    4715         [ +  + ]:         118 :         else if (node->operation == CMD_MERGE)
    4716                 :             :         {
    4717                 :             :                 /* EXPLAIN ANALYZE display of tuples processed */
    4718   [ +  +  -  + ]:          30 :                 if (es->analyze && mtstate->ps.instrument)
    4719                 :             :                 {
    4720                 :           8 :                         double          total;
    4721                 :           8 :                         double          insert_path;
    4722                 :           8 :                         double          update_path;
    4723                 :           8 :                         double          delete_path;
    4724                 :           8 :                         double          skipped_path;
    4725                 :             : 
    4726                 :           8 :                         InstrEndLoop(outerPlanState(mtstate)->instrument);
    4727                 :             : 
    4728                 :             :                         /* count the number of source rows */
    4729                 :           8 :                         total = outerPlanState(mtstate)->instrument->ntuples;
    4730                 :           8 :                         insert_path = mtstate->mt_merge_inserted;
    4731                 :           8 :                         update_path = mtstate->mt_merge_updated;
    4732                 :           8 :                         delete_path = mtstate->mt_merge_deleted;
    4733                 :           8 :                         skipped_path = total - insert_path - update_path - delete_path;
    4734         [ +  - ]:           8 :                         Assert(skipped_path >= 0);
    4735                 :             : 
    4736         [ -  + ]:           8 :                         if (es->format == EXPLAIN_FORMAT_TEXT)
    4737                 :             :                         {
    4738         [ +  + ]:           8 :                                 if (total > 0)
    4739                 :             :                                 {
    4740                 :           7 :                                         ExplainIndentText(es);
    4741                 :           7 :                                         appendStringInfoString(es->str, "Tuples:");
    4742         [ +  + ]:           7 :                                         if (insert_path > 0)
    4743                 :           2 :                                                 appendStringInfo(es->str, " inserted=%.0f", insert_path);
    4744         [ +  + ]:           7 :                                         if (update_path > 0)
    4745                 :           4 :                                                 appendStringInfo(es->str, " updated=%.0f", update_path);
    4746         [ +  + ]:           7 :                                         if (delete_path > 0)
    4747                 :           2 :                                                 appendStringInfo(es->str, " deleted=%.0f", delete_path);
    4748         [ +  + ]:           7 :                                         if (skipped_path > 0)
    4749                 :           6 :                                                 appendStringInfo(es->str, " skipped=%.0f", skipped_path);
    4750                 :           7 :                                         appendStringInfoChar(es->str, '\n');
    4751                 :           7 :                                 }
    4752                 :           8 :                         }
    4753                 :             :                         else
    4754                 :             :                         {
    4755                 :           0 :                                 ExplainPropertyFloat("Tuples Inserted", NULL, insert_path, 0, es);
    4756                 :           0 :                                 ExplainPropertyFloat("Tuples Updated", NULL, update_path, 0, es);
    4757                 :           0 :                                 ExplainPropertyFloat("Tuples Deleted", NULL, delete_path, 0, es);
    4758                 :           0 :                                 ExplainPropertyFloat("Tuples Skipped", NULL, skipped_path, 0, es);
    4759                 :             :                         }
    4760                 :           8 :                 }
    4761                 :          30 :         }
    4762                 :             : 
    4763         [ +  + ]:         142 :         if (labeltargets)
    4764                 :          36 :                 ExplainCloseGroup("Target Tables", "Target Tables", false, es);
    4765                 :         142 : }
    4766                 :             : 
    4767                 :             : /*
    4768                 :             :  * Explain what a "Result" node replaced.
    4769                 :             :  */
    4770                 :             : static void
    4771                 :         426 : show_result_replacement_info(Result *result, ExplainState *es)
    4772                 :             : {
    4773                 :         426 :         StringInfoData buf;
    4774                 :         426 :         int                     nrels = 0;
    4775                 :         426 :         int                     rti = -1;
    4776                 :         426 :         bool            found_non_result = false;
    4777                 :         426 :         char       *replacement_type = "???";
    4778                 :             : 
    4779                 :             :         /* If the Result node has a subplan, it didn't replace anything. */
    4780         [ +  + ]:         426 :         if (result->plan.lefttree != NULL)
    4781                 :          37 :                 return;
    4782                 :             : 
    4783                 :             :         /* Gating result nodes should have a subplan, and we don't. */
    4784         [ +  - ]:         389 :         Assert(result->result_type != RESULT_TYPE_GATING);
    4785                 :             : 
    4786   [ -  +  +  +  :         389 :         switch (result->result_type)
                   -  + ]
    4787                 :             :         {
    4788                 :             :                 case RESULT_TYPE_GATING:
    4789                 :           0 :                         replacement_type = "Gating";
    4790                 :           0 :                         break;
    4791                 :             :                 case RESULT_TYPE_SCAN:
    4792                 :         332 :                         replacement_type = "Scan";
    4793                 :         332 :                         break;
    4794                 :             :                 case RESULT_TYPE_JOIN:
    4795                 :          20 :                         replacement_type = "Join";
    4796                 :          20 :                         break;
    4797                 :             :                 case RESULT_TYPE_UPPER:
    4798                 :             :                         /* a small white lie */
    4799                 :          11 :                         replacement_type = "Aggregate";
    4800                 :          11 :                         break;
    4801                 :             :                 case RESULT_TYPE_MINMAX:
    4802                 :          26 :                         replacement_type = "MinMaxAggregate";
    4803                 :          26 :                         break;
    4804                 :             :         }
    4805                 :             : 
    4806                 :             :         /*
    4807                 :             :          * Build up a comma-separated list of user-facing names for the range
    4808                 :             :          * table entries in the relids set.
    4809                 :             :          */
    4810                 :         389 :         initStringInfo(&buf);
    4811         [ +  + ]:         819 :         while ((rti = bms_next_member(result->relids, rti)) >= 0)
    4812                 :             :         {
    4813                 :         430 :                 RangeTblEntry *rte = rt_fetch(rti, es->rtable);
    4814                 :         430 :                 char       *refname;
    4815                 :             : 
    4816                 :             :                 /*
    4817                 :             :                  * add_outer_joins_to_relids will add join RTIs to the relids set of a
    4818                 :             :                  * join; if that join is then replaced with a Result node, we may see
    4819                 :             :                  * such RTIs here. But we want to completely ignore those here,
    4820                 :             :                  * because "a LEFT JOIN b ON whatever" is a join between a and b, not
    4821                 :             :                  * a join between a, b, and an unnamed join.
    4822                 :             :                  */
    4823         [ +  + ]:         430 :                 if (rte->rtekind == RTE_JOIN)
    4824                 :          20 :                         continue;
    4825                 :             : 
    4826                 :             :                 /* Count the number of rels that aren't ignored completely. */
    4827                 :         410 :                 ++nrels;
    4828                 :             : 
    4829                 :             :                 /* Work out what reference name to use and add it to the string. */
    4830                 :         410 :                 refname = (char *) list_nth(es->rtable_names, rti - 1);
    4831         [ +  - ]:         410 :                 if (refname == NULL)
    4832                 :           0 :                         refname = rte->eref->aliasname;
    4833         [ +  + ]:         410 :                 if (buf.len > 0)
    4834                 :          53 :                         appendStringInfoString(&buf, ", ");
    4835                 :         410 :                 appendStringInfoString(&buf, refname);
    4836                 :             : 
    4837                 :             :                 /* Keep track of whether we see anything other than RTE_RESULT. */
    4838         [ +  + ]:         410 :                 if (rte->rtekind != RTE_RESULT)
    4839                 :         149 :                         found_non_result = true;
    4840         [ +  + ]:         430 :         }
    4841                 :             : 
    4842                 :             :         /*
    4843                 :             :          * If this Result node is because of a single RTE that is RTE_RESULT, it
    4844                 :             :          * is not really replacing anything at all, because there's no other
    4845                 :             :          * method for implementing a scan of such an RTE, so we don't display the
    4846                 :             :          * Replaces line in such cases.
    4847                 :             :          */
    4848   [ +  +  +  +  :         389 :         if (nrels <= 1 && !found_non_result &&
                   +  + ]
    4849                 :         292 :                 result->result_type == RESULT_TYPE_SCAN)
    4850                 :         260 :                 return;
    4851                 :             : 
    4852                 :             :         /* Say what we replaced, with list of rels if available. */
    4853         [ +  + ]:         129 :         if (buf.len == 0)
    4854                 :          32 :                 ExplainPropertyText("Replaces", replacement_type, es);
    4855                 :             :         else
    4856                 :             :         {
    4857                 :          97 :                 char       *s = psprintf("%s on %s", replacement_type, buf.data);
    4858                 :             : 
    4859                 :          97 :                 ExplainPropertyText("Replaces", s, es);
    4860                 :          97 :         }
    4861                 :         426 : }
    4862                 :             : 
    4863                 :             : /*
    4864                 :             :  * Explain the constituent plans of an Append, MergeAppend,
    4865                 :             :  * BitmapAnd, or BitmapOr node.
    4866                 :             :  *
    4867                 :             :  * The ancestors list should already contain the immediate parent of these
    4868                 :             :  * plans.
    4869                 :             :  */
    4870                 :             : static void
    4871                 :         664 : ExplainMemberNodes(PlanState **planstates, int nplans,
    4872                 :             :                                    List *ancestors, ExplainState *es)
    4873                 :             : {
    4874                 :         664 :         int                     j;
    4875                 :             : 
    4876         [ +  + ]:        2703 :         for (j = 0; j < nplans; j++)
    4877                 :        4078 :                 ExplainNode(planstates[j], ancestors,
    4878                 :        2039 :                                         "Member", NULL, es);
    4879                 :         664 : }
    4880                 :             : 
    4881                 :             : /*
    4882                 :             :  * Report about any pruned subnodes of an Append or MergeAppend node.
    4883                 :             :  *
    4884                 :             :  * nplans indicates the number of live subplans.
    4885                 :             :  * nchildren indicates the original number of subnodes in the Plan;
    4886                 :             :  * some of these may have been pruned by the run-time pruning code.
    4887                 :             :  */
    4888                 :             : static void
    4889                 :         633 : ExplainMissingMembers(int nplans, int nchildren, ExplainState *es)
    4890                 :             : {
    4891   [ +  +  -  + ]:         633 :         if (nplans < nchildren || es->format != EXPLAIN_FORMAT_TEXT)
    4892                 :          41 :                 ExplainPropertyInteger("Subplans Removed", NULL,
    4893                 :          41 :                                                            nchildren - nplans, es);
    4894                 :         633 : }
    4895                 :             : 
    4896                 :             : /*
    4897                 :             :  * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
    4898                 :             :  *
    4899                 :             :  * The ancestors list should already contain the immediate parent of these
    4900                 :             :  * SubPlans.
    4901                 :             :  */
    4902                 :             : static void
    4903                 :         220 : ExplainSubPlans(List *plans, List *ancestors,
    4904                 :             :                                 const char *relationship, ExplainState *es)
    4905                 :             : {
    4906                 :         220 :         ListCell   *lst;
    4907                 :             : 
    4908   [ +  -  +  +  :         470 :         foreach(lst, plans)
                   +  + ]
    4909                 :             :         {
    4910                 :         250 :                 SubPlanState *sps = (SubPlanState *) lfirst(lst);
    4911                 :         250 :                 SubPlan    *sp = sps->subplan;
    4912                 :         250 :                 char       *cooked_plan_name;
    4913                 :             : 
    4914                 :             :                 /*
    4915                 :             :                  * There can be multiple SubPlan nodes referencing the same physical
    4916                 :             :                  * subplan (same plan_id, which is its index in PlannedStmt.subplans).
    4917                 :             :                  * We should print a subplan only once, so track which ones we already
    4918                 :             :                  * printed.  This state must be global across the plan tree, since the
    4919                 :             :                  * duplicate nodes could be in different plan nodes, eg both a bitmap
    4920                 :             :                  * indexscan's indexqual and its parent heapscan's recheck qual.  (We
    4921                 :             :                  * do not worry too much about which plan node we show the subplan as
    4922                 :             :                  * attached to in such cases.)
    4923                 :             :                  */
    4924         [ +  + ]:         250 :                 if (bms_is_member(sp->plan_id, es->printed_subplans))
    4925                 :          15 :                         continue;
    4926                 :         470 :                 es->printed_subplans = bms_add_member(es->printed_subplans,
    4927                 :         235 :                                                                                           sp->plan_id);
    4928                 :             : 
    4929                 :             :                 /*
    4930                 :             :                  * Treat the SubPlan node as an ancestor of the plan node(s) within
    4931                 :             :                  * it, so that ruleutils.c can find the referents of subplan
    4932                 :             :                  * parameters.
    4933                 :             :                  */
    4934                 :         235 :                 ancestors = lcons(sp, ancestors);
    4935                 :             : 
    4936                 :             :                 /*
    4937                 :             :                  * The plan has a name like exists_1 or rowcompare_2, but here we want
    4938                 :             :                  * to prefix that with CTE, InitPlan, or SubPlan, as appropriate, for
    4939                 :             :                  * display purposes.
    4940                 :             :                  */
    4941         [ +  + ]:         235 :                 if (sp->subLinkType == CTE_SUBLINK)
    4942                 :          39 :                         cooked_plan_name = psprintf("CTE %s", sp->plan_name);
    4943         [ +  + ]:         196 :                 else if (sp->isInitPlan)
    4944                 :          97 :                         cooked_plan_name = psprintf("InitPlan %s", sp->plan_name);
    4945                 :             :                 else
    4946                 :          99 :                         cooked_plan_name = psprintf("SubPlan %s", sp->plan_name);
    4947                 :             : 
    4948                 :         470 :                 ExplainNode(sps->planstate, ancestors,
    4949                 :         235 :                                         relationship, cooked_plan_name, es);
    4950                 :             : 
    4951                 :         235 :                 ancestors = list_delete_first(ancestors);
    4952      [ -  +  + ]:         250 :         }
    4953                 :         220 : }
    4954                 :             : 
    4955                 :             : /*
    4956                 :             :  * Explain a list of children of a CustomScan.
    4957                 :             :  */
    4958                 :             : static void
    4959                 :           0 : ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es)
    4960                 :             : {
    4961                 :           0 :         ListCell   *cell;
    4962                 :           0 :         const char *label =
    4963                 :           0 :                 (list_length(css->custom_ps) != 1 ? "children" : "child");
    4964                 :             : 
    4965   [ #  #  #  #  :           0 :         foreach(cell, css->custom_ps)
                   #  # ]
    4966                 :           0 :                 ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
    4967                 :           0 : }
    4968                 :             : 
    4969                 :             : /*
    4970                 :             :  * Create a per-plan-node workspace for collecting per-worker data.
    4971                 :             :  *
    4972                 :             :  * Output related to each worker will be temporarily "set aside" into a
    4973                 :             :  * separate buffer, which we'll merge into the main output stream once
    4974                 :             :  * we've processed all data for the plan node.  This makes it feasible to
    4975                 :             :  * generate a coherent sub-group of fields for each worker, even though the
    4976                 :             :  * code that produces the fields is in several different places in this file.
    4977                 :             :  * Formatting of such a set-aside field group is managed by
    4978                 :             :  * ExplainOpenSetAsideGroup and ExplainSaveGroup/ExplainRestoreGroup.
    4979                 :             :  */
    4980                 :             : static ExplainWorkersState *
    4981                 :         171 : ExplainCreateWorkersState(int num_workers)
    4982                 :             : {
    4983                 :         171 :         ExplainWorkersState *wstate;
    4984                 :             : 
    4985                 :         171 :         wstate = palloc_object(ExplainWorkersState);
    4986                 :         171 :         wstate->num_workers = num_workers;
    4987                 :         171 :         wstate->worker_inited = (bool *) palloc0(num_workers * sizeof(bool));
    4988                 :         171 :         wstate->worker_str = (StringInfoData *)
    4989                 :         171 :                 palloc0(num_workers * sizeof(StringInfoData));
    4990                 :         171 :         wstate->worker_state_save = (int *) palloc(num_workers * sizeof(int));
    4991                 :         342 :         return wstate;
    4992                 :         171 : }
    4993                 :             : 
    4994                 :             : /*
    4995                 :             :  * Begin or resume output into the set-aside group for worker N.
    4996                 :             :  */
    4997                 :             : static void
    4998                 :          24 : ExplainOpenWorker(int n, ExplainState *es)
    4999                 :             : {
    5000                 :          24 :         ExplainWorkersState *wstate = es->workers_state;
    5001                 :             : 
    5002         [ +  - ]:          24 :         Assert(wstate);
    5003         [ +  - ]:          24 :         Assert(n >= 0 && n < wstate->num_workers);
    5004                 :             : 
    5005                 :             :         /* Save prior output buffer pointer */
    5006                 :          24 :         wstate->prev_str = es->str;
    5007                 :             : 
    5008         [ +  + ]:          24 :         if (!wstate->worker_inited[n])
    5009                 :             :         {
    5010                 :             :                 /* First time through, so create the buffer for this worker */
    5011                 :          12 :                 initStringInfo(&wstate->worker_str[n]);
    5012                 :          12 :                 es->str = &wstate->worker_str[n];
    5013                 :             : 
    5014                 :             :                 /*
    5015                 :             :                  * Push suitable initial formatting state for this worker's field
    5016                 :             :                  * group.  We allow one extra logical nesting level, since this group
    5017                 :             :                  * will eventually be wrapped in an outer "Workers" group.
    5018                 :             :                  */
    5019                 :          12 :                 ExplainOpenSetAsideGroup("Worker", NULL, true, 2, es);
    5020                 :             : 
    5021                 :             :                 /*
    5022                 :             :                  * In non-TEXT formats we always emit a "Worker Number" field, even if
    5023                 :             :                  * there's no other data for this worker.
    5024                 :             :                  */
    5025         [ +  + ]:          12 :                 if (es->format != EXPLAIN_FORMAT_TEXT)
    5026                 :           8 :                         ExplainPropertyInteger("Worker Number", NULL, n, es);
    5027                 :             : 
    5028                 :          12 :                 wstate->worker_inited[n] = true;
    5029                 :          12 :         }
    5030                 :             :         else
    5031                 :             :         {
    5032                 :             :                 /* Resuming output for a worker we've already emitted some data for */
    5033                 :          12 :                 es->str = &wstate->worker_str[n];
    5034                 :             : 
    5035                 :             :                 /* Restore formatting state saved by last ExplainCloseWorker() */
    5036                 :          12 :                 ExplainRestoreGroup(es, 2, &wstate->worker_state_save[n]);
    5037                 :             :         }
    5038                 :             : 
    5039                 :             :         /*
    5040                 :             :          * In TEXT format, prefix the first output line for this worker with
    5041                 :             :          * "Worker N:".  Then, any additional lines should be indented one more
    5042                 :             :          * stop than the "Worker N" line is.
    5043                 :             :          */
    5044         [ +  + ]:          24 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    5045                 :             :         {
    5046         [ -  + ]:           4 :                 if (es->str->len == 0)
    5047                 :             :                 {
    5048                 :           4 :                         ExplainIndentText(es);
    5049                 :           4 :                         appendStringInfo(es->str, "Worker %d:  ", n);
    5050                 :           4 :                 }
    5051                 :             : 
    5052                 :           4 :                 es->indent++;
    5053                 :           4 :         }
    5054                 :          24 : }
    5055                 :             : 
    5056                 :             : /*
    5057                 :             :  * End output for worker N --- must pair with previous ExplainOpenWorker call
    5058                 :             :  */
    5059                 :             : static void
    5060                 :          24 : ExplainCloseWorker(int n, ExplainState *es)
    5061                 :             : {
    5062                 :          24 :         ExplainWorkersState *wstate = es->workers_state;
    5063                 :             : 
    5064         [ +  - ]:          24 :         Assert(wstate);
    5065         [ +  - ]:          24 :         Assert(n >= 0 && n < wstate->num_workers);
    5066         [ +  - ]:          24 :         Assert(wstate->worker_inited[n]);
    5067                 :             : 
    5068                 :             :         /*
    5069                 :             :          * Save formatting state in case we do another ExplainOpenWorker(), then
    5070                 :             :          * pop the formatting stack.
    5071                 :             :          */
    5072                 :          24 :         ExplainSaveGroup(es, 2, &wstate->worker_state_save[n]);
    5073                 :             : 
    5074                 :             :         /*
    5075                 :             :          * In TEXT format, if we didn't actually produce any output line(s) then
    5076                 :             :          * truncate off the partial line emitted by ExplainOpenWorker.  (This is
    5077                 :             :          * to avoid bogus output if, say, show_buffer_usage chooses not to print
    5078                 :             :          * anything for the worker.)  Also fix up the indent level.
    5079                 :             :          */
    5080         [ +  + ]:          24 :         if (es->format == EXPLAIN_FORMAT_TEXT)
    5081                 :             :         {
    5082   [ -  +  -  + ]:           4 :                 while (es->str->len > 0 && es->str->data[es->str->len - 1] != '\n')
    5083                 :           0 :                         es->str->data[--(es->str->len)] = '\0';
    5084                 :             : 
    5085                 :           4 :                 es->indent--;
    5086                 :           4 :         }
    5087                 :             : 
    5088                 :             :         /* Restore prior output buffer pointer */
    5089                 :          24 :         es->str = wstate->prev_str;
    5090                 :          24 : }
    5091                 :             : 
    5092                 :             : /*
    5093                 :             :  * Print per-worker info for current node, then free the ExplainWorkersState.
    5094                 :             :  */
    5095                 :             : static void
    5096                 :         171 : ExplainFlushWorkersState(ExplainState *es)
    5097                 :             : {
    5098                 :         171 :         ExplainWorkersState *wstate = es->workers_state;
    5099                 :             : 
    5100                 :         171 :         ExplainOpenGroup("Workers", "Workers", false, es);
    5101         [ +  + ]:         451 :         for (int i = 0; i < wstate->num_workers; i++)
    5102                 :             :         {
    5103         [ +  + ]:         280 :                 if (wstate->worker_inited[i])
    5104                 :             :                 {
    5105                 :             :                         /* This must match previous ExplainOpenSetAsideGroup call */
    5106                 :          12 :                         ExplainOpenGroup("Worker", NULL, true, es);
    5107                 :          12 :                         appendStringInfoString(es->str, wstate->worker_str[i].data);
    5108                 :          12 :                         ExplainCloseGroup("Worker", NULL, true, es);
    5109                 :             : 
    5110                 :          12 :                         pfree(wstate->worker_str[i].data);
    5111                 :          12 :                 }
    5112                 :         280 :         }
    5113                 :         171 :         ExplainCloseGroup("Workers", "Workers", false, es);
    5114                 :             : 
    5115                 :         171 :         pfree(wstate->worker_inited);
    5116                 :         171 :         pfree(wstate->worker_str);
    5117                 :         171 :         pfree(wstate->worker_state_save);
    5118                 :         171 :         pfree(wstate);
    5119                 :         171 : }
        

Generated by: LCOV version 2.3.2-1