LCOV - code coverage report
Current view: top level - contrib/pg_overexplain - pg_overexplain.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 62.4 % 412 257
Test Date: 2026-05-04 14:18:02 Functions: 100.0 % 13 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * pg_overexplain.c
       4              :  *    allow EXPLAIN to dump even more details
       5              :  *
       6              :  * Copyright (c) 2016-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  *    contrib/pg_overexplain/pg_overexplain.c
       9              :  *-------------------------------------------------------------------------
      10              :  */
      11              : #include "postgres.h"
      12              : 
      13              : #include "catalog/pg_class.h"
      14              : #include "commands/defrem.h"
      15              : #include "commands/explain.h"
      16              : #include "commands/explain_format.h"
      17              : #include "commands/explain_state.h"
      18              : #include "fmgr.h"
      19              : #include "parser/parsetree.h"
      20              : #include "storage/lock.h"
      21              : #include "utils/builtins.h"
      22              : #include "utils/lsyscache.h"
      23              : 
      24           15 : PG_MODULE_MAGIC_EXT(
      25              :                     .name = "pg_overexplain",
      26              :                     .version = PG_VERSION
      27              : );
      28              : 
      29              : typedef struct
      30              : {
      31              :     bool        debug;
      32              :     bool        range_table;
      33              : } overexplain_options;
      34              : 
      35              : static overexplain_options *overexplain_ensure_options(ExplainState *es);
      36              : static void overexplain_debug_handler(ExplainState *es, DefElem *opt,
      37              :                                       ParseState *pstate);
      38              : static void overexplain_range_table_handler(ExplainState *es, DefElem *opt,
      39              :                                             ParseState *pstate);
      40              : static void overexplain_per_node_hook(PlanState *planstate, List *ancestors,
      41              :                                       const char *relationship,
      42              :                                       const char *plan_name,
      43              :                                       ExplainState *es);
      44              : static void overexplain_per_plan_hook(PlannedStmt *plannedstmt,
      45              :                                       IntoClause *into,
      46              :                                       ExplainState *es,
      47              :                                       const char *queryString,
      48              :                                       ParamListInfo params,
      49              :                                       QueryEnvironment *queryEnv);
      50              : static void overexplain_debug(PlannedStmt *plannedstmt, ExplainState *es);
      51              : static void overexplain_range_table(PlannedStmt *plannedstmt,
      52              :                                     ExplainState *es);
      53              : static void overexplain_alias(const char *qlabel, Alias *alias,
      54              :                               ExplainState *es);
      55              : static void overexplain_bitmapset(const char *qlabel, Bitmapset *bms,
      56              :                                   ExplainState *es);
      57              : static void overexplain_bitmapset_list(const char *qlabel, List *bms_list,
      58              :                                        ExplainState *es);
      59              : static void overexplain_intlist(const char *qlabel, List *list,
      60              :                                 ExplainState *es);
      61              : 
      62              : static int  es_extension_id;
      63              : static explain_per_node_hook_type prev_explain_per_node_hook;
      64              : static explain_per_plan_hook_type prev_explain_per_plan_hook;
      65              : 
      66              : /*
      67              :  * Initialization we do when this module is loaded.
      68              :  */
      69              : void
      70           15 : _PG_init(void)
      71              : {
      72              :     /* Get an ID that we can use to cache data in an ExplainState. */
      73           15 :     es_extension_id = GetExplainExtensionId("pg_overexplain");
      74              : 
      75              :     /* Register the new EXPLAIN options implemented by this module. */
      76           15 :     RegisterExtensionExplainOption("debug", overexplain_debug_handler,
      77              :                                    GUCCheckBooleanExplainOption);
      78           15 :     RegisterExtensionExplainOption("range_table",
      79              :                                    overexplain_range_table_handler,
      80              :                                    GUCCheckBooleanExplainOption);
      81              : 
      82              :     /* Use the per-node and per-plan hooks to make our options do something. */
      83           15 :     prev_explain_per_node_hook = explain_per_node_hook;
      84           15 :     explain_per_node_hook = overexplain_per_node_hook;
      85           15 :     prev_explain_per_plan_hook = explain_per_plan_hook;
      86           15 :     explain_per_plan_hook = overexplain_per_plan_hook;
      87           15 : }
      88              : 
      89              : /*
      90              :  * Get the overexplain_options structure from an ExplainState; if there is
      91              :  * none, create one, attach it to the ExplainState, and return it.
      92              :  */
      93              : static overexplain_options *
      94           16 : overexplain_ensure_options(ExplainState *es)
      95              : {
      96              :     overexplain_options *options;
      97              : 
      98           16 :     options = GetExplainExtensionState(es, es_extension_id);
      99              : 
     100           16 :     if (options == NULL)
     101              :     {
     102           14 :         options = palloc0_object(overexplain_options);
     103           14 :         SetExplainExtensionState(es, es_extension_id, options);
     104              :     }
     105              : 
     106           16 :     return options;
     107              : }
     108              : 
     109              : /*
     110              :  * Parse handler for EXPLAIN (DEBUG).
     111              :  */
     112              : static void
     113            7 : overexplain_debug_handler(ExplainState *es, DefElem *opt, ParseState *pstate)
     114              : {
     115            7 :     overexplain_options *options = overexplain_ensure_options(es);
     116              : 
     117            7 :     options->debug = defGetBoolean(opt);
     118            7 : }
     119              : 
     120              : /*
     121              :  * Parse handler for EXPLAIN (RANGE_TABLE).
     122              :  */
     123              : static void
     124            9 : overexplain_range_table_handler(ExplainState *es, DefElem *opt,
     125              :                                 ParseState *pstate)
     126              : {
     127            9 :     overexplain_options *options = overexplain_ensure_options(es);
     128              : 
     129            9 :     options->range_table = defGetBoolean(opt);
     130            9 : }
     131              : 
     132              : /*
     133              :  * Print out additional per-node information as appropriate. If the user didn't
     134              :  * specify any of the options we support, do nothing; else, print whatever is
     135              :  * relevant to the specified options.
     136              :  */
     137              : static void
     138           59 : overexplain_per_node_hook(PlanState *planstate, List *ancestors,
     139              :                           const char *relationship, const char *plan_name,
     140              :                           ExplainState *es)
     141              : {
     142              :     overexplain_options *options;
     143           59 :     Plan       *plan = planstate->plan;
     144              : 
     145           59 :     if (prev_explain_per_node_hook)
     146            0 :         (*prev_explain_per_node_hook) (planstate, ancestors, relationship,
     147              :                                        plan_name, es);
     148              : 
     149           59 :     options = GetExplainExtensionState(es, es_extension_id);
     150           59 :     if (options == NULL)
     151           10 :         return;
     152              : 
     153              :     /*
     154              :      * If the "debug" option was given, display miscellaneous fields from the
     155              :      * "Plan" node that would not otherwise be displayed.
     156              :      */
     157           49 :     if (options->debug)
     158              :     {
     159              :         /*
     160              :          * Normal EXPLAIN will display "Disabled: true" if the node is
     161              :          * disabled; but that is based on noticing that plan->disabled_nodes
     162              :          * is higher than the sum of its children; here, we display the raw
     163              :          * value, for debugging purposes.
     164              :          */
     165           27 :         ExplainPropertyInteger("Disabled Nodes", NULL, plan->disabled_nodes,
     166              :                                es);
     167              : 
     168              :         /*
     169              :          * Normal EXPLAIN will display the parallel_aware flag; here, we show
     170              :          * the parallel_safe flag as well.
     171              :          */
     172           27 :         ExplainPropertyBool("Parallel Safe", plan->parallel_safe, es);
     173              : 
     174              :         /*
     175              :          * The plan node ID isn't normally displayed, since it is only useful
     176              :          * for debugging.
     177              :          */
     178           27 :         ExplainPropertyInteger("Plan Node ID", NULL, plan->plan_node_id, es);
     179              : 
     180              :         /*
     181              :          * It is difficult to explain what extParam and allParam mean in plain
     182              :          * language, so we simply display these fields labelled with the
     183              :          * structure member name. For compactness, the text format omits the
     184              :          * display of this information when the bitmapset is empty.
     185              :          */
     186           27 :         if (es->format != EXPLAIN_FORMAT_TEXT || !bms_is_empty(plan->extParam))
     187            8 :             overexplain_bitmapset("extParam", plan->extParam, es);
     188           27 :         if (es->format != EXPLAIN_FORMAT_TEXT || !bms_is_empty(plan->allParam))
     189            8 :             overexplain_bitmapset("allParam", plan->allParam, es);
     190              :     }
     191              : 
     192              :     /*
     193              :      * If the "range_table" option was specified, display information about
     194              :      * the range table indexes for this node.
     195              :      */
     196           49 :     if (options->range_table)
     197              :     {
     198           32 :         bool        opened_elided_nodes = false;
     199              : 
     200           32 :         switch (nodeTag(plan))
     201              :         {
     202           15 :             case T_SeqScan:
     203              :             case T_SampleScan:
     204              :             case T_IndexScan:
     205              :             case T_IndexOnlyScan:
     206              :             case T_BitmapHeapScan:
     207              :             case T_TidScan:
     208              :             case T_TidRangeScan:
     209              :             case T_SubqueryScan:
     210              :             case T_FunctionScan:
     211              :             case T_TableFuncScan:
     212              :             case T_ValuesScan:
     213              :             case T_CteScan:
     214              :             case T_NamedTuplestoreScan:
     215              :             case T_WorkTableScan:
     216           15 :                 ExplainPropertyInteger("Scan RTI", NULL,
     217           15 :                                        ((Scan *) plan)->scanrelid, es);
     218           15 :                 break;
     219            0 :             case T_ForeignScan:
     220            0 :                 overexplain_bitmapset("Scan RTIs",
     221              :                                       ((ForeignScan *) plan)->fs_base_relids,
     222              :                                       es);
     223            0 :                 break;
     224            0 :             case T_CustomScan:
     225            0 :                 overexplain_bitmapset("Scan RTIs",
     226              :                                       ((CustomScan *) plan)->custom_relids,
     227              :                                       es);
     228            0 :                 break;
     229            1 :             case T_ModifyTable:
     230            1 :                 ExplainPropertyInteger("Nominal RTI", NULL,
     231            1 :                                        ((ModifyTable *) plan)->nominalRelation, es);
     232            1 :                 ExplainPropertyInteger("Exclude Relation RTI", NULL,
     233            1 :                                        ((ModifyTable *) plan)->exclRelRTI, es);
     234            1 :                 break;
     235            6 :             case T_Append:
     236            6 :                 overexplain_bitmapset("Append RTIs",
     237              :                                       ((Append *) plan)->apprelids,
     238              :                                       es);
     239            6 :                 overexplain_bitmapset_list("Child Append RTIs",
     240              :                                            ((Append *) plan)->child_append_relid_sets,
     241              :                                            es);
     242            6 :                 break;
     243            0 :             case T_MergeAppend:
     244            0 :                 overexplain_bitmapset("Append RTIs",
     245              :                                       ((MergeAppend *) plan)->apprelids,
     246              :                                       es);
     247            0 :                 overexplain_bitmapset_list("Child Append RTIs",
     248              :                                            ((MergeAppend *) plan)->child_append_relid_sets,
     249              :                                            es);
     250            0 :                 break;
     251            2 :             case T_Result:
     252              : 
     253              :                 /*
     254              :                  * 'relids' is only meaningful when plan->lefttree is NULL,
     255              :                  * but if somehow it ends up set when plan->lefttree is not
     256              :                  * NULL, print it anyway.
     257              :                  */
     258            2 :                 if (plan->lefttree == NULL ||
     259            0 :                     ((Result *) plan)->relids != NULL)
     260            2 :                     overexplain_bitmapset("RTIs",
     261              :                                           ((Result *) plan)->relids,
     262              :                                           es);
     263            2 :                 break;
     264            8 :             default:
     265            8 :                 break;
     266              :         }
     267              : 
     268           91 :         foreach_node(ElidedNode, n, es->pstmt->elidedNodes)
     269              :         {
     270              :             char       *elidednodetag;
     271              : 
     272           27 :             if (n->plan_node_id != plan->plan_node_id)
     273           20 :                 continue;
     274              : 
     275            7 :             if (!opened_elided_nodes)
     276              :             {
     277            5 :                 ExplainOpenGroup("Elided Nodes", "Elided Nodes", false, es);
     278            5 :                 opened_elided_nodes = true;
     279              :             }
     280              : 
     281            7 :             switch (n->elided_type)
     282              :             {
     283            3 :                 case T_Append:
     284            3 :                     elidednodetag = "Append";
     285            3 :                     break;
     286            0 :                 case T_MergeAppend:
     287            0 :                     elidednodetag = "MergeAppend";
     288            0 :                     break;
     289            4 :                 case T_SubqueryScan:
     290            4 :                     elidednodetag = "SubqueryScan";
     291            4 :                     break;
     292            0 :                 default:
     293            0 :                     elidednodetag = psprintf("%d", n->elided_type);
     294            0 :                     break;
     295              :             }
     296              : 
     297            7 :             ExplainOpenGroup("Elided Node", NULL, true, es);
     298            7 :             ExplainPropertyText("Elided Node Type", elidednodetag, es);
     299            7 :             overexplain_bitmapset("Elided Node RTIs", n->relids, es);
     300            7 :             ExplainCloseGroup("Elided Node", NULL, true, es);
     301              :         }
     302           32 :         if (opened_elided_nodes)
     303            5 :             ExplainCloseGroup("Elided Nodes", "Elided Nodes", false, es);
     304              :     }
     305              : }
     306              : 
     307              : /*
     308              :  * Print out additional per-query information as appropriate. Here again, if
     309              :  * the user didn't specify any of the options implemented by this module, do
     310              :  * nothing; otherwise, call the appropriate function for each specified
     311              :  * option.
     312              :  */
     313              : static void
     314           24 : overexplain_per_plan_hook(PlannedStmt *plannedstmt,
     315              :                           IntoClause *into,
     316              :                           ExplainState *es,
     317              :                           const char *queryString,
     318              :                           ParamListInfo params,
     319              :                           QueryEnvironment *queryEnv)
     320              : {
     321              :     overexplain_options *options;
     322              : 
     323           24 :     if (prev_explain_per_plan_hook)
     324            0 :         (*prev_explain_per_plan_hook) (plannedstmt, into, es, queryString,
     325              :                                        params, queryEnv);
     326              : 
     327           24 :     options = GetExplainExtensionState(es, es_extension_id);
     328           24 :     if (options == NULL)
     329           10 :         return;
     330              : 
     331           14 :     if (options->debug)
     332            7 :         overexplain_debug(plannedstmt, es);
     333              : 
     334           14 :     if (options->range_table)
     335            9 :         overexplain_range_table(plannedstmt, es);
     336              : }
     337              : 
     338              : /*
     339              :  * Print out various details from the PlannedStmt that wouldn't otherwise
     340              :  * be displayed.
     341              :  *
     342              :  * We don't try to print everything here. Information that would be displayed
     343              :  * anyway doesn't need to be printed again here, and things with lots of
     344              :  * substructure probably should be printed via separate options, or not at all.
     345              :  */
     346              : static void
     347            7 : overexplain_debug(PlannedStmt *plannedstmt, ExplainState *es)
     348              : {
     349            7 :     char       *commandType = NULL;
     350              :     StringInfoData flags;
     351              : 
     352              :     /* Even in text mode, we want to set this output apart as its own group. */
     353            7 :     ExplainOpenGroup("PlannedStmt", "PlannedStmt", true, es);
     354            7 :     if (es->format == EXPLAIN_FORMAT_TEXT)
     355              :     {
     356            6 :         ExplainIndentText(es);
     357            6 :         appendStringInfoString(es->str, "PlannedStmt:\n");
     358            6 :         es->indent++;
     359              :     }
     360              : 
     361              :     /* Print the command type. */
     362            7 :     switch (plannedstmt->commandType)
     363              :     {
     364            0 :         case CMD_UNKNOWN:
     365            0 :             commandType = "unknown";
     366            0 :             break;
     367            6 :         case CMD_SELECT:
     368            6 :             commandType = "select";
     369            6 :             break;
     370            0 :         case CMD_UPDATE:
     371            0 :             commandType = "update";
     372            0 :             break;
     373            1 :         case CMD_INSERT:
     374            1 :             commandType = "insert";
     375            1 :             break;
     376            0 :         case CMD_DELETE:
     377            0 :             commandType = "delete";
     378            0 :             break;
     379            0 :         case CMD_MERGE:
     380            0 :             commandType = "merge";
     381            0 :             break;
     382            0 :         case CMD_UTILITY:
     383            0 :             commandType = "utility";
     384            0 :             break;
     385            0 :         case CMD_NOTHING:
     386            0 :             commandType = "nothing";
     387            0 :             break;
     388              :     }
     389            7 :     ExplainPropertyText("Command Type", commandType, es);
     390              : 
     391              :     /* Print various properties as a comma-separated list of flags. */
     392            7 :     initStringInfo(&flags);
     393            7 :     if (plannedstmt->hasReturning)
     394            1 :         appendStringInfoString(&flags, ", hasReturning");
     395            7 :     if (plannedstmt->hasModifyingCTE)
     396            0 :         appendStringInfoString(&flags, ", hasModifyingCTE");
     397            7 :     if (plannedstmt->canSetTag)
     398            7 :         appendStringInfoString(&flags, ", canSetTag");
     399            7 :     if (plannedstmt->transientPlan)
     400            0 :         appendStringInfoString(&flags, ", transientPlan");
     401            7 :     if (plannedstmt->dependsOnRole)
     402            0 :         appendStringInfoString(&flags, ", dependsOnRole");
     403            7 :     if (plannedstmt->parallelModeNeeded)
     404            1 :         appendStringInfoString(&flags, ", parallelModeNeeded");
     405            7 :     if (flags.len == 0)
     406            0 :         appendStringInfoString(&flags, ", none");
     407            7 :     ExplainPropertyText("Flags", flags.data + 2, es);
     408              : 
     409              :     /* Various lists of integers. */
     410            7 :     overexplain_bitmapset("Subplans Needing Rewind",
     411              :                           plannedstmt->rewindPlanIDs, es);
     412            7 :     overexplain_intlist("Relation OIDs",
     413              :                         plannedstmt->relationOids, es);
     414            7 :     overexplain_intlist("Executor Parameter Types",
     415              :                         plannedstmt->paramExecTypes, es);
     416              : 
     417              :     /*
     418              :      * Print the statement location. (If desired, we could alternatively print
     419              :      * stmt_location and stmt_len as two separate fields.)
     420              :      */
     421            7 :     if (plannedstmt->stmt_location == -1)
     422            0 :         ExplainPropertyText("Parse Location", "Unknown", es);
     423            7 :     else if (plannedstmt->stmt_len == 0)
     424            6 :         ExplainPropertyText("Parse Location",
     425            6 :                             psprintf("%d to end", plannedstmt->stmt_location),
     426              :                             es);
     427              :     else
     428            1 :         ExplainPropertyText("Parse Location",
     429            1 :                             psprintf("%d for %d bytes",
     430              :                                      plannedstmt->stmt_location,
     431              :                                      plannedstmt->stmt_len),
     432              :                             es);
     433              : 
     434              :     /* Done with this group. */
     435            7 :     if (es->format == EXPLAIN_FORMAT_TEXT)
     436            6 :         es->indent--;
     437            7 :     ExplainCloseGroup("PlannedStmt", "PlannedStmt", true, es);
     438            7 : }
     439              : 
     440              : /*
     441              :  * Provide detailed information about the contents of the PlannedStmt's
     442              :  * range table.
     443              :  */
     444              : static void
     445            9 : overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
     446              : {
     447              :     Index       rti;
     448            9 :     ListCell   *lc_subrtinfo = list_head(plannedstmt->subrtinfos);
     449            9 :     SubPlanRTInfo *rtinfo = NULL;
     450              : 
     451              :     /* Open group, one entry per RangeTblEntry */
     452            9 :     ExplainOpenGroup("Range Table", "Range Table", false, es);
     453              : 
     454              :     /* Iterate over the range table */
     455           43 :     for (rti = 1; rti <= list_length(plannedstmt->rtable); ++rti)
     456              :     {
     457           34 :         RangeTblEntry *rte = rt_fetch(rti, plannedstmt->rtable);
     458           34 :         char       *kind = NULL;
     459              :         char       *relkind;
     460              :         SubPlanRTInfo *next_rtinfo;
     461              : 
     462              :         /* Advance to next SubPlanRTInfo, if it's time. */
     463           34 :         if (lc_subrtinfo != NULL)
     464              :         {
     465           15 :             next_rtinfo = lfirst(lc_subrtinfo);
     466           15 :             if (rti > next_rtinfo->rtoffset)
     467              :             {
     468            4 :                 rtinfo = next_rtinfo;
     469            4 :                 lc_subrtinfo = lnext(plannedstmt->subrtinfos, lc_subrtinfo);
     470              :             }
     471              :         }
     472              : 
     473              :         /* NULL entries are possible; skip them */
     474           34 :         if (rte == NULL)
     475            0 :             continue;
     476              : 
     477              :         /* Translate rtekind to a string */
     478           34 :         switch (rte->rtekind)
     479              :         {
     480           24 :             case RTE_RELATION:
     481           24 :                 kind = "relation";
     482           24 :                 break;
     483            5 :             case RTE_SUBQUERY:
     484            5 :                 kind = "subquery";
     485            5 :                 break;
     486            0 :             case RTE_JOIN:
     487            0 :                 kind = "join";
     488            0 :                 break;
     489            0 :             case RTE_FUNCTION:
     490            0 :                 kind = "function";
     491            0 :                 break;
     492            0 :             case RTE_TABLEFUNC:
     493            0 :                 kind = "tablefunc";
     494            0 :                 break;
     495            0 :             case RTE_VALUES:
     496            0 :                 kind = "values";
     497            0 :                 break;
     498            0 :             case RTE_CTE:
     499            0 :                 kind = "cte";
     500            0 :                 break;
     501            0 :             case RTE_NAMEDTUPLESTORE:
     502            0 :                 kind = "namedtuplestore";
     503            0 :                 break;
     504            2 :             case RTE_RESULT:
     505            2 :                 kind = "result";
     506            2 :                 break;
     507            3 :             case RTE_GROUP:
     508            3 :                 kind = "group";
     509            3 :                 break;
     510            0 :             case RTE_GRAPH_TABLE:
     511              : 
     512              :                 /*
     513              :                  * We should not see RTE of this kind here since property
     514              :                  * graph RTE gets converted to subquery RTE in
     515              :                  * rewriteGraphTable(). In case we decide not to do the
     516              :                  * conversion and leave RTE kind unchanged in future, print
     517              :                  * correct name of RTE kind.
     518              :                  */
     519            0 :                 kind = "graph_table";
     520            0 :                 break;
     521              :         }
     522              : 
     523              :         /* Begin group for this specific RTE */
     524           34 :         ExplainOpenGroup("Range Table Entry", NULL, true, es);
     525              : 
     526              :         /*
     527              :          * In text format, the summary line displays the range table index and
     528              :          * rtekind, plus indications if rte->inh and/or rte->inFromCl are set.
     529              :          * In other formats, we display those as separate properties.
     530              :          */
     531           34 :         if (es->format == EXPLAIN_FORMAT_TEXT)
     532              :         {
     533           26 :             ExplainIndentText(es);
     534           52 :             appendStringInfo(es->str, "RTI %u (%s%s%s):\n", rti, kind,
     535           26 :                              rte->inh ? ", inherited" : "",
     536           26 :                              rte->inFromCl ? ", in-from-clause" : "");
     537           26 :             es->indent++;
     538              :         }
     539              :         else
     540              :         {
     541            8 :             ExplainPropertyUInteger("RTI", NULL, rti, es);
     542            8 :             ExplainPropertyText("Kind", kind, es);
     543            8 :             ExplainPropertyBool("Inherited", rte->inh, es);
     544            8 :             ExplainPropertyBool("In From Clause", rte->inFromCl, es);
     545              :         }
     546              : 
     547              :         /*
     548              :          * Indicate which subplan is the origin of which RTE. Note dummy
     549              :          * subplans. Here again, we crunch more onto one line in text format.
     550              :          */
     551           34 :         if (rtinfo != NULL)
     552              :         {
     553            6 :             if (es->format == EXPLAIN_FORMAT_TEXT)
     554              :             {
     555            6 :                 if (!rtinfo->dummy)
     556            6 :                     ExplainPropertyText("Subplan", rtinfo->plan_name, es);
     557              :                 else
     558            0 :                     ExplainPropertyText("Subplan",
     559            0 :                                         psprintf("%s (dummy)",
     560              :                                                  rtinfo->plan_name), es);
     561              :             }
     562              :             else
     563              :             {
     564            0 :                 ExplainPropertyText("Subplan", rtinfo->plan_name, es);
     565            0 :                 ExplainPropertyBool("Subplan Is Dummy", rtinfo->dummy, es);
     566              :             }
     567              :         }
     568              : 
     569              :         /* rte->alias is optional; rte->eref is requested */
     570           34 :         if (rte->alias != NULL)
     571           16 :             overexplain_alias("Alias", rte->alias, es);
     572           34 :         overexplain_alias("Eref", rte->eref, es);
     573              : 
     574              :         /*
     575              :          * We adhere to the usual EXPLAIN convention that schema names are
     576              :          * displayed only in verbose mode, and we emit nothing if there is no
     577              :          * relation OID.
     578              :          */
     579           34 :         if (rte->relid != 0)
     580              :         {
     581              :             const char *relname;
     582              :             const char *qualname;
     583              : 
     584           25 :             relname = quote_identifier(get_rel_name(rte->relid));
     585              : 
     586           25 :             if (es->verbose)
     587              :             {
     588            0 :                 Oid         nspoid = get_rel_namespace(rte->relid);
     589              :                 char       *nspname;
     590              : 
     591            0 :                 nspname = get_namespace_name_or_temp(nspoid);
     592            0 :                 qualname = psprintf("%s.%s", quote_identifier(nspname),
     593              :                                     relname);
     594              :             }
     595              :             else
     596           25 :                 qualname = relname;
     597              : 
     598           25 :             ExplainPropertyText("Relation", qualname, es);
     599              :         }
     600              : 
     601              :         /* Translate relkind, if any, to a string */
     602           34 :         switch (rte->relkind)
     603              :         {
     604           15 :             case RELKIND_RELATION:
     605           15 :                 relkind = "relation";
     606           15 :                 break;
     607            0 :             case RELKIND_INDEX:
     608            0 :                 relkind = "index";
     609            0 :                 break;
     610            0 :             case RELKIND_SEQUENCE:
     611            0 :                 relkind = "sequence";
     612            0 :                 break;
     613            0 :             case RELKIND_TOASTVALUE:
     614            0 :                 relkind = "toastvalue";
     615            0 :                 break;
     616            0 :             case RELKIND_VIEW:
     617            0 :                 relkind = "view";
     618            0 :                 break;
     619            0 :             case RELKIND_MATVIEW:
     620            0 :                 relkind = "matview";
     621            0 :                 break;
     622            0 :             case RELKIND_COMPOSITE_TYPE:
     623            0 :                 relkind = "composite_type";
     624            0 :                 break;
     625            0 :             case RELKIND_FOREIGN_TABLE:
     626            0 :                 relkind = "foreign_table";
     627            0 :                 break;
     628            9 :             case RELKIND_PARTITIONED_TABLE:
     629            9 :                 relkind = "partitioned_table";
     630            9 :                 break;
     631            0 :             case RELKIND_PARTITIONED_INDEX:
     632            0 :                 relkind = "partitioned_index";
     633            0 :                 break;
     634            1 :             case RELKIND_PROPGRAPH:
     635            1 :                 relkind = "property_graph";
     636            1 :                 break;
     637            9 :             case '\0':
     638            9 :                 relkind = NULL;
     639            9 :                 break;
     640            0 :             default:
     641            0 :                 relkind = psprintf("%c", rte->relkind);
     642            0 :                 break;
     643              :         }
     644              : 
     645              :         /* If there is a relkind, show it */
     646           34 :         if (relkind != NULL)
     647           25 :             ExplainPropertyText("Relation Kind", relkind, es);
     648              : 
     649              :         /* If there is a lock mode, show it */
     650           34 :         if (rte->rellockmode != 0)
     651           25 :             ExplainPropertyText("Relation Lock Mode",
     652              :                                 GetLockmodeName(DEFAULT_LOCKMETHOD,
     653              :                                                 rte->rellockmode), es);
     654              : 
     655              :         /*
     656              :          * If there is a perminfoindex, show it. We don't try to display
     657              :          * information from the RTEPermissionInfo node here because they are
     658              :          * just indexes plannedstmt->permInfos which could be separately
     659              :          * dumped if someone wants to add EXPLAIN (PERMISSIONS) or similar.
     660              :          */
     661           34 :         if (rte->perminfoindex != 0)
     662           12 :             ExplainPropertyInteger("Permission Info Index", NULL,
     663           12 :                                    rte->perminfoindex, es);
     664              : 
     665              :         /*
     666              :          * add_rte_to_flat_rtable will clear rte->tablesample and
     667              :          * rte->subquery in the finished plan, so skip those fields.
     668              :          *
     669              :          * However, the security_barrier flag is not shown by the core code,
     670              :          * so let's print it here.
     671              :          */
     672           34 :         if (es->format != EXPLAIN_FORMAT_TEXT || rte->security_barrier)
     673            8 :             ExplainPropertyBool("Security Barrier", rte->security_barrier, es);
     674              : 
     675              :         /*
     676              :          * If this is a join, print out the fields that are specifically valid
     677              :          * for joins.
     678              :          */
     679           34 :         if (rte->rtekind == RTE_JOIN)
     680              :         {
     681              :             char       *jointype;
     682              : 
     683            0 :             switch (rte->jointype)
     684              :             {
     685            0 :                 case JOIN_INNER:
     686            0 :                     jointype = "Inner";
     687            0 :                     break;
     688            0 :                 case JOIN_LEFT:
     689            0 :                     jointype = "Left";
     690            0 :                     break;
     691            0 :                 case JOIN_FULL:
     692            0 :                     jointype = "Full";
     693            0 :                     break;
     694            0 :                 case JOIN_RIGHT:
     695            0 :                     jointype = "Right";
     696            0 :                     break;
     697            0 :                 case JOIN_SEMI:
     698            0 :                     jointype = "Semi";
     699            0 :                     break;
     700            0 :                 case JOIN_ANTI:
     701            0 :                     jointype = "Anti";
     702            0 :                     break;
     703            0 :                 case JOIN_RIGHT_SEMI:
     704            0 :                     jointype = "Right Semi";
     705            0 :                     break;
     706            0 :                 case JOIN_RIGHT_ANTI:
     707            0 :                     jointype = "Right Anti";
     708            0 :                     break;
     709            0 :                 default:
     710            0 :                     jointype = "???";
     711            0 :                     break;
     712              :             }
     713              : 
     714              :             /* Join type */
     715            0 :             ExplainPropertyText("Join Type", jointype, es);
     716              : 
     717              :             /* # of JOIN USING columns */
     718            0 :             if (es->format != EXPLAIN_FORMAT_TEXT || rte->joinmergedcols != 0)
     719            0 :                 ExplainPropertyInteger("JOIN USING Columns", NULL,
     720            0 :                                        rte->joinmergedcols, es);
     721              : 
     722              :             /*
     723              :              * add_rte_to_flat_rtable will clear joinaliasvars, joinleftcols,
     724              :              * joinrightcols, and join_using_alias here, so skip those fields.
     725              :              */
     726              :         }
     727              : 
     728              :         /*
     729              :          * add_rte_to_flat_rtable will clear functions, tablefunc, and
     730              :          * values_lists, but we can display funcordinality.
     731              :          */
     732           34 :         if (rte->rtekind == RTE_FUNCTION)
     733            0 :             ExplainPropertyBool("WITH ORDINALITY", rte->funcordinality, es);
     734              : 
     735              :         /*
     736              :          * If this is a CTE, print out CTE-related properties.
     737              :          */
     738           34 :         if (rte->rtekind == RTE_CTE)
     739              :         {
     740            0 :             ExplainPropertyText("CTE Name", rte->ctename, es);
     741            0 :             ExplainPropertyUInteger("CTE Levels Up", NULL, rte->ctelevelsup,
     742              :                                     es);
     743            0 :             ExplainPropertyBool("CTE Self-Reference", rte->self_reference, es);
     744              :         }
     745              : 
     746              :         /*
     747              :          * add_rte_to_flat_rtable will clear coltypes, coltypmods, and
     748              :          * colcollations, so skip those fields.
     749              :          *
     750              :          * If this is an ephemeral named relation, print out ENR-related
     751              :          * properties.
     752              :          */
     753           34 :         if (rte->rtekind == RTE_NAMEDTUPLESTORE)
     754              :         {
     755            0 :             ExplainPropertyText("ENR Name", rte->enrname, es);
     756            0 :             ExplainPropertyFloat("ENR Tuples", NULL, rte->enrtuples, 0, es);
     757              :         }
     758              : 
     759              :         /*
     760              :          * rewriteGraphTable() clears graph_pattern and graph_table_columns
     761              :          * fields, so skip them. No graph table specific fields are required
     762              :          * to be printed.
     763              :          */
     764              : 
     765              :         /*
     766              :          * add_rte_to_flat_rtable will clear groupexprs and securityQuals, so
     767              :          * skip that field. We have handled inFromCl above, so the only thing
     768              :          * left to handle here is rte->lateral.
     769              :          */
     770           34 :         if (es->format != EXPLAIN_FORMAT_TEXT || rte->lateral)
     771           11 :             ExplainPropertyBool("Lateral", rte->lateral, es);
     772              : 
     773              :         /* Done with this RTE */
     774           34 :         if (es->format == EXPLAIN_FORMAT_TEXT)
     775           26 :             es->indent--;
     776           34 :         ExplainCloseGroup("Range Table Entry", NULL, true, es);
     777              :     }
     778              : 
     779              :     /* Close the Range Table array before emitting PlannedStmt-level fields. */
     780            9 :     ExplainCloseGroup("Range Table", "Range Table", false, es);
     781              : 
     782              :     /*
     783              :      * Print PlannedStmt fields that contain RTIs.  These are properties of
     784              :      * the PlannedStmt, not of individual RTEs, so they belong outside the
     785              :      * Range Table array.
     786              :      */
     787            9 :     if (es->format != EXPLAIN_FORMAT_TEXT ||
     788            7 :         !bms_is_empty(plannedstmt->unprunableRelids))
     789            8 :         overexplain_bitmapset("Unprunable RTIs", plannedstmt->unprunableRelids,
     790              :                               es);
     791            9 :     if (es->format != EXPLAIN_FORMAT_TEXT ||
     792            7 :         !bms_is_empty(plannedstmt->resultRelationRelids))
     793            3 :         overexplain_bitmapset("Result RTIs", plannedstmt->resultRelationRelids,
     794              :                               es);
     795            9 : }
     796              : 
     797              : /*
     798              :  * Emit a text property describing the contents of an Alias.
     799              :  *
     800              :  * Column lists can be quite long here, so perhaps we should have an option
     801              :  * to limit the display length by # of column or # of characters, but for
     802              :  * now, just display everything.
     803              :  */
     804              : static void
     805           50 : overexplain_alias(const char *qlabel, Alias *alias, ExplainState *es)
     806              : {
     807              :     StringInfoData buf;
     808           50 :     bool        first = true;
     809              : 
     810              :     Assert(alias != NULL);
     811              : 
     812           50 :     initStringInfo(&buf);
     813           50 :     appendStringInfo(&buf, "%s (", quote_identifier(alias->aliasname));
     814              : 
     815          223 :     foreach_node(String, cn, alias->colnames)
     816              :     {
     817          123 :         appendStringInfo(&buf, "%s%s",
     818              :                          first ? "" : ", ",
     819          123 :                          quote_identifier(cn->sval));
     820          123 :         first = false;
     821              :     }
     822              : 
     823           50 :     appendStringInfoChar(&buf, ')');
     824           50 :     ExplainPropertyText(qlabel, buf.data, es);
     825           50 :     pfree(buf.data);
     826           50 : }
     827              : 
     828              : /*
     829              :  * Emit a text property describing the contents of a bitmapset -- either a
     830              :  * space-separated list of integer members, or the word "none" if the bitmapset
     831              :  * is empty.
     832              :  */
     833              : static void
     834           49 : overexplain_bitmapset(const char *qlabel, Bitmapset *bms, ExplainState *es)
     835              : {
     836           49 :     int         x = -1;
     837              : 
     838              :     StringInfoData buf;
     839              : 
     840           49 :     if (bms_is_empty(bms))
     841              :     {
     842           19 :         ExplainPropertyText(qlabel, "none", es);
     843           19 :         return;
     844              :     }
     845              : 
     846           30 :     initStringInfo(&buf);
     847           77 :     while ((x = bms_next_member(bms, x)) >= 0)
     848           47 :         appendStringInfo(&buf, " %d", x);
     849              :     Assert(buf.data[0] == ' ');
     850           30 :     ExplainPropertyText(qlabel, buf.data + 1, es);
     851           30 :     pfree(buf.data);
     852              : }
     853              : 
     854              : /*
     855              :  * Emit a text property describing the contents of a list of bitmapsets.
     856              :  * If a bitmapset contains exactly 1 member, we just print an integer;
     857              :  * otherwise, we surround the list of members by parentheses.
     858              :  *
     859              :  * If there are no bitmapsets in the list, we print the word "none".
     860              :  */
     861              : static void
     862            6 : overexplain_bitmapset_list(const char *qlabel, List *bms_list,
     863              :                            ExplainState *es)
     864              : {
     865              :     StringInfoData buf;
     866              : 
     867            6 :     initStringInfo(&buf);
     868              : 
     869           12 :     foreach_node(Bitmapset, bms, bms_list)
     870              :     {
     871            0 :         if (bms_membership(bms) == BMS_SINGLETON)
     872            0 :             appendStringInfo(&buf, " %d", bms_singleton_member(bms));
     873              :         else
     874              :         {
     875            0 :             int         x = -1;
     876            0 :             bool        first = true;
     877              : 
     878            0 :             appendStringInfoString(&buf, " (");
     879            0 :             while ((x = bms_next_member(bms, x)) >= 0)
     880              :             {
     881            0 :                 if (first)
     882            0 :                     first = false;
     883              :                 else
     884            0 :                     appendStringInfoChar(&buf, ' ');
     885            0 :                 appendStringInfo(&buf, "%d", x);
     886              :             }
     887            0 :             appendStringInfoChar(&buf, ')');
     888              :         }
     889              :     }
     890              : 
     891            6 :     if (buf.len == 0)
     892              :     {
     893            6 :         ExplainPropertyText(qlabel, "none", es);
     894            6 :         return;
     895              :     }
     896              : 
     897              :     Assert(buf.data[0] == ' ');
     898            0 :     ExplainPropertyText(qlabel, buf.data + 1, es);
     899            0 :     pfree(buf.data);
     900              : }
     901              : 
     902              : /*
     903              :  * Emit a text property describing the contents of a list of integers, OIDs,
     904              :  * or XIDs -- either a space-separated list of integer members, or the word
     905              :  * "none" if the list is empty.
     906              :  */
     907              : static void
     908           14 : overexplain_intlist(const char *qlabel, List *list, ExplainState *es)
     909              : {
     910              :     StringInfoData buf;
     911              : 
     912           14 :     initStringInfo(&buf);
     913              : 
     914           14 :     if (list == NIL)
     915              :     {
     916            7 :         ExplainPropertyText(qlabel, "none", es);
     917            7 :         return;
     918              :     }
     919              : 
     920            7 :     if (IsA(list, IntList))
     921              :     {
     922            0 :         foreach_int(i, list)
     923            0 :             appendStringInfo(&buf, " %d", i);
     924              :     }
     925            7 :     else if (IsA(list, OidList))
     926              :     {
     927           33 :         foreach_oid(o, list)
     928           19 :             appendStringInfo(&buf, " %u", o);
     929              :     }
     930            0 :     else if (IsA(list, XidList))
     931              :     {
     932            0 :         foreach_xid(x, list)
     933            0 :             appendStringInfo(&buf, " %u", x);
     934              :     }
     935              :     else
     936              :     {
     937            0 :         appendStringInfoString(&buf, " not an integer list");
     938              :         Assert(false);
     939              :     }
     940              : 
     941            7 :     if (buf.len > 0)
     942            7 :         ExplainPropertyText(qlabel, buf.data + 1, es);
     943              : 
     944            7 :     pfree(buf.data);
     945              : }
        

Generated by: LCOV version 2.0-1