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

Generated by: LCOV version 2.3.2-1