1/*-------------------------------------------------------------------------
4 * allow EXPLAIN to dump even more details
6 * Copyright (c) 2016-2025, PostgreSQL Global Development Group
8 * contrib/pg_overexplain/pg_overexplain.c
9 *-------------------------------------------------------------------------
25 .
name =
"pg_overexplain",
41 const char *relationship,
42 const char *plan_name,
47 const char *queryString,
65 * Initialization we do when this module is loaded.
70 /* Get an ID that we can use to cache data in an ExplainState. */
73 /* Register the new EXPLAIN options implemented by this module. */
78 /* Use the per-node and per-plan hooks to make our options do something. */
86 * Get the overexplain_options structure from an ExplainState; if there is
87 * none, create one, attach it to the ExplainState, and return it.
106 * Parse handler for EXPLAIN (DEBUG).
117 * Parse handler for EXPLAIN (RANGE_TABLE).
129 * Print out additional per-node information as appropriate. If the user didn't
130 * specify any of the options we support, do nothing; else, print whatever is
131 * relevant to the specified options.
135 const char *relationship,
const char *plan_name,
142 (*prev_explain_per_node_hook) (planstate, ancestors, relationship,
150 * If the "debug" option was given, display miscellaneous fields from the
151 * "Plan" node that would not otherwise be displayed.
156 * Normal EXPLAIN will display "Disabled: true" if the node is
157 * disabled; but that is based on noticing that plan->disabled_nodes
158 * is higher than the sum of its children; here, we display the raw
159 * value, for debugging purposes.
165 * Normal EXPLAIN will display the parallel_aware flag; here, we show
166 * the parallel_safe flag as well.
171 * The plan node ID isn't normally displayed, since it is only useful
177 * It is difficult to explain what extParam and allParam mean in plain
178 * language, so we simply display these fields labelled with the
179 * structure member name. For compactness, the text format omits the
180 * display of this information when the bitmapset is empty.
189 * If the "range_table" option was specified, display information about
190 * the range table indexes for this node.
199 case T_IndexOnlyScan:
200 case T_BitmapHeapScan:
205 case T_TableFuncScan:
208 case T_NamedTuplestoreScan:
209 case T_WorkTableScan:
242 * 'relids' is only meaningful when plan->lefttree is NULL,
243 * but if somehow it ends up set when plan->lefttree is not
244 * NULL, print it anyway.
246 if (
plan->lefttree == NULL ||
258 * Print out additional per-query information as appropriate. Here again, if
259 * the user didn't specify any of the options implemented by this module, do
260 * nothing; otherwise, call the appropriate function for each specified
267 const char *queryString,
274 (*prev_explain_per_plan_hook) (plannedstmt, into, es, queryString,
289 * Print out various details from the PlannedStmt that wouldn't otherwise
292 * We don't try to print everything here. Information that would be displayed
293 * anyway doesn't need to be printed again here, and things with lots of
294 * substructure probably should be printed via separate options, or not at all.
299 char *commandType = NULL;
302 /* Even in text mode, we want to set this output apart as its own group. */
311 /* Print the command type. */
315 commandType =
"unknown";
318 commandType =
"select";
321 commandType =
"update";
324 commandType =
"insert";
327 commandType =
"delete";
330 commandType =
"merge";
333 commandType =
"utility";
336 commandType =
"nothing";
341 /* Print various properties as a comma-separated list of flags. */
359 /* Various lists of integers. */
368 * Print the statement location. (If desired, we could alternatively print
369 * stmt_location and stmt_len as two separate fields.)
373 else if (plannedstmt->
stmt_len == 0)
384 /* Done with this group. */
391 * Provide detailed information about the contents of the PlannedStmt's
399 /* Open group, one entry per RangeTblEntry */
402 /* Iterate over the range table */
409 /* NULL entries are possible; skip them */
413 /* Translate rtekind to a string */
438 kind =
"namedtuplestore";
448 /* Begin group for this specific RTE */
452 * In text format, the summary line displays the range table index and
453 * rtekind, plus indications if rte->inh and/or rte->inFromCl are set.
454 * In other formats, we display those as separate properties.
460 rte->
inh ?
", inherited" :
"",
461 rte->inFromCl ?
", in-from-clause" :
"");
472 /* rte->alias is optional; rte->eref is requested */
473 if (rte->alias != NULL)
478 * We adhere to the usual EXPLAIN convention that schema names are
479 * displayed only in verbose mode, and we emit nothing if there is no
485 const char *qualname;
504 /* Translate relkind, if any, to a string */
505 switch (rte->relkind)
507 case RELKIND_RELATION:
508 relkind =
"relation";
513 case RELKIND_SEQUENCE:
514 relkind =
"sequence";
516 case RELKIND_TOASTVALUE:
517 relkind =
"toastvalue";
522 case RELKIND_MATVIEW:
525 case RELKIND_COMPOSITE_TYPE:
526 relkind =
"composite_type";
528 case RELKIND_FOREIGN_TABLE:
529 relkind =
"foreign_table";
531 case RELKIND_PARTITIONED_TABLE:
532 relkind =
"partitioned_table";
534 case RELKIND_PARTITIONED_INDEX:
535 relkind =
"partitioned_index";
541 relkind =
psprintf(
"%c", rte->relkind);
545 /* If there is a relkind, show it */
549 /* If there is a lock mode, show it */
550 if (rte->rellockmode != 0)
553 rte->rellockmode), es);
556 * If there is a perminfoindex, show it. We don't try to display
557 * information from the RTEPermissionInfo node here because they are
558 * just indexes plannedstmt->permInfos which could be separately
559 * dumped if someone wants to add EXPLAIN (PERMISSIONS) or similar.
561 if (rte->perminfoindex != 0)
563 rte->perminfoindex, es);
566 * add_rte_to_flat_rtable will clear rte->tablesample and
567 * rte->subquery in the finished plan, so skip those fields.
569 * However, the security_barrier flag is not shown by the core code,
570 * so let's print it here.
576 * If this is a join, print out the fields that are specifically valid
604 jointype =
"Right Semi";
607 jointype =
"Right Anti";
617 /* # of JOIN USING columns */
620 rte->joinmergedcols, es);
623 * add_rte_to_flat_rtable will clear joinaliasvars, joinleftcols,
624 * joinrightcols, and join_using_alias here, so skip those fields.
629 * add_rte_to_flat_rtable will clear functions, tablefunc, and
630 * values_lists, but we can display funcordinality.
636 * If this is a CTE, print out CTE-related properties.
647 * add_rte_to_flat_rtable will clear coltypes, coltypmods, and
648 * colcollations, so skip those fields.
650 * If this is an ephemeral named relation, print out ENR-related
660 * add_rte_to_flat_rtable will clear groupexprs and securityQuals, so
661 * skip that field. We have handled inFromCl above, so the only thing
662 * left to handle here is rte->lateral.
667 /* Done with this RTE */
673 /* Print PlannedStmt fields that contain RTIs. */
682 /* Close group, we're all done */
687 * Emit a text property describing the contents of an Alias.
689 * Column lists can be quite long here, so perhaps we should have an option
690 * to limit the display length by # of column or # of characters, but for
691 * now, just display everything.
718 * Emit a text property describing the contents of a bitmapset -- either a
719 * space-separated list of integer members, or the word "none" if the bitmapset
744 * Emit a text property describing the contents of a list of integers, OIDs,
745 * or XIDs -- either a space-separated list of integer members, or the word
746 * "none" if the list is empty.
int bms_next_member(const Bitmapset *a, int prevbit)
bool defGetBoolean(DefElem *def)
explain_per_node_hook_type explain_per_node_hook
explain_per_plan_hook_type explain_per_plan_hook
void(* explain_per_plan_hook_type)(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
void(* explain_per_node_hook_type)(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
int GetExplainExtensionId(const char *extension_name)
void * GetExplainExtensionState(ExplainState *es, int extension_id)
void SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque)
void RegisterExtensionExplainOption(const char *option_name, ExplainOptionHandler handler)
Assert(PointerIsAligned(start, uint64))
const char * GetLockmodeName(LOCKMETHODID lockmethodid, LOCKMODE mode)
#define DEFAULT_LOCKMETHOD
char * get_rel_name(Oid relid)
Oid get_rel_namespace(Oid relid)
char * get_namespace_name_or_temp(Oid nspid)
void pfree(void *pointer)
void * palloc0(Size size)
#define IsA(nodeptr, _type_)
#define rt_fetch(rangetable_index, rangetable)
static int list_length(const List *l)
#define foreach_xid(var, lst)
#define foreach_node(type, var, lst)
#define foreach_oid(var, lst)
#define foreach_int(var, lst)
static void overexplain_per_plan_hook(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
static void overexplain_bitmapset(const char *qlabel, Bitmapset *bms, ExplainState *es)
static void overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
static explain_per_plan_hook_type prev_explain_per_plan_hook
static explain_per_node_hook_type prev_explain_per_node_hook
static overexplain_options * overexplain_ensure_options(ExplainState *es)
static void overexplain_per_node_hook(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
static void overexplain_intlist(const char *qlabel, List *list, ExplainState *es)
PG_MODULE_MAGIC_EXT(.name="pg_overexplain",.version=PG_VERSION)
static void overexplain_debug_handler(ExplainState *es, DefElem *opt, ParseState *pstate)
static void overexplain_alias(const char *qlabel, Alias *alias, ExplainState *es)
static int es_extension_id
static void overexplain_debug(PlannedStmt *plannedstmt, ExplainState *es)
static void overexplain_range_table_handler(ExplainState *es, DefElem *opt, ParseState *pstate)
char * psprintf(const char *fmt,...)
const char * quote_identifier(const char *ident)
void appendStringInfo(StringInfo str, const char *fmt,...)
void appendStringInfoString(StringInfo str, const char *s)
void appendStringInfoChar(StringInfo str, char ch)
void initStringInfo(StringInfo str)
Bitmapset * rewindPlanIDs
Bitmapset * unprunableRelids