diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index fe43942f3b7..c50ec0ca207 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -33,6 +33,10 @@ #include "utils/xml.h" +/* Crude hack to avoid changing sizeof(ExplainState) in released branches */ +#define grouping_stack extra->groupingstack +#define deparse_cxt extra->deparsecxt + /* Hook for plugins to get control in ExplainOneQuery() */ ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL; @@ -253,6 +257,8 @@ ExplainInitState(ExplainState *es) es->costs = true; /* Prepare output buffer. */ es->str = makeStringInfo(); + /* Kluge to avoid changing sizeof(ExplainState) in released branches. */ + es->extra = (ExplainStateExtra *) palloc0(sizeof(ExplainStateExtra)); } /* @@ -555,6 +561,8 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc) es->rtable = queryDesc->plannedstmt->rtable; ExplainPreScanNode(queryDesc->planstate, &rels_used); es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used); + es->deparse_cxt = deparse_context_for_plan_rtable(es->rtable, + es->rtable_names); ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es); } @@ -1585,10 +1593,9 @@ show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es) return; /* Set up deparsing context */ - context = deparse_context_for_planstate((Node *) planstate, - ancestors, - es->rtable, - es->rtable_names); + context = set_deparse_context_planstate(es->deparse_cxt, + (Node *) planstate, + ancestors); useprefix = list_length(es->rtable) > 1; /* Deparse each result column (we now include resjunk ones) */ @@ -1617,10 +1624,9 @@ show_expression(Node *node, const char *qlabel, char *exprstr; /* Set up deparsing context */ - context = deparse_context_for_planstate((Node *) planstate, - ancestors, - es->rtable, - es->rtable_names); + context = set_deparse_context_planstate(es->deparse_cxt, + (Node *) planstate, + ancestors); /* Deparse the expression */ exprstr = deparse_expression(node, context, useprefix, false); @@ -1720,10 +1726,9 @@ show_sort_keys_common(PlanState *planstate, int nkeys, AttrNumber *keycols, return; /* Set up deparsing context */ - context = deparse_context_for_planstate((Node *) planstate, - ancestors, - es->rtable, - es->rtable_names); + context = set_deparse_context_planstate(es->deparse_cxt, + (Node *) planstate, + ancestors); useprefix = (list_length(es->rtable) > 1 || es->verbose); for (keyno = 0; keyno < nkeys; keyno++) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 1a71a444ced..18256a1bb82 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2370,7 +2370,43 @@ deparse_context_for(const char *aliasname, Oid relid) } /* - * deparse_context_for_planstate - Build deparse context for a plan + * deparse_context_for_plan_rtable - Build deparse context for a plan's rtable + * + * When deparsing an expression in a Plan tree, we use the plan's rangetable + * to resolve names of simple Vars. The initialization of column names for + * this is rather expensive if the rangetable is large, and it'll be the same + * for every expression in the Plan tree; so we do it just once and re-use + * the result of this function for each expression. (Note that the result + * is not usable until set_deparse_context_planstate() is applied to it.) + * + * In addition to the plan's rangetable list, pass the per-RTE alias names + * assigned by a previous call to select_rtable_names_for_explain. + */ +List * +deparse_context_for_plan_rtable(List *rtable, List *rtable_names) +{ + deparse_namespace *dpns; + + dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace)); + + /* Initialize fields that stay the same across the whole plan tree */ + dpns->rtable = rtable; + dpns->rtable_names = rtable_names; + dpns->ctes = NIL; + + /* + * Set up column name aliases. We will get rather bogus results for join + * RTEs, but that doesn't matter because plan trees don't contain any join + * alias Vars. + */ + set_simple_column_names(dpns); + + /* Return a one-deep namespace stack */ + return list_make1(dpns); +} + +/* + * set_deparse_context_planstate - Specify Plan node containing expression * * When deparsing an expression in a Plan tree, we might have to resolve * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must @@ -2389,37 +2425,28 @@ deparse_context_for(const char *aliasname, Oid relid) * most-closely-nested first. This is needed to resolve PARAM_EXEC Params. * Note we assume that all the PlanStates share the same rtable. * - * The plan's rangetable list must also be passed, along with the per-RTE - * alias names assigned by a previous call to select_rtable_names_for_explain. - * (We use the rangetable to resolve simple Vars, but the plan inputs are - * necessary for Vars with special varnos.) + * Once this function has been called, deparse_expression() can be called on + * subsidiary expression(s) of the specified PlanState node. To deparse + * expressions of a different Plan node in the same Plan tree, re-call this + * function to identify the new parent Plan node. + * + * The result is the same List passed in; this is a notational convenience. */ List * -deparse_context_for_planstate(Node *planstate, List *ancestors, - List *rtable, List *rtable_names) +set_deparse_context_planstate(List *dpcontext, + Node *planstate, List *ancestors) { deparse_namespace *dpns; - dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace)); - - /* Initialize fields that stay the same across the whole plan tree */ - dpns->rtable = rtable; - dpns->rtable_names = rtable_names; - dpns->ctes = NIL; - - /* - * Set up column name aliases. We will get rather bogus results for join - * RTEs, but that doesn't matter because plan trees don't contain any join - * alias Vars. - */ - set_simple_column_names(dpns); + /* Should always have one-entry namespace list for Plan deparsing */ + Assert(list_length(dpcontext) == 1); + dpns = (deparse_namespace *) linitial(dpcontext); /* Set our attention on the specific plan node passed in */ set_deparse_planstate(dpns, (PlanState *) planstate); dpns->ancestors = ancestors; - /* Return a one-deep namespace stack */ - return list_make1(dpns); + return dpcontext; } /* diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index ca213d7f704..664499c2d35 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -24,6 +24,13 @@ typedef enum ExplainFormat EXPLAIN_FORMAT_YAML } ExplainFormat; +/* Crude hack to avoid changing sizeof(ExplainState) in released branches */ +typedef struct ExplainStateExtra +{ + List *groupingstack; /* format-specific grouping state */ + List *deparsecxt; /* context list for deparsing expressions */ +} ExplainStateExtra; + typedef struct ExplainState { StringInfo str; /* output buffer */ @@ -39,7 +46,7 @@ typedef struct ExplainState List *rtable; /* range table */ List *rtable_names; /* alias names for RTEs */ int indent; /* current indentation level */ - List *grouping_stack; /* format-specific grouping state */ + ExplainStateExtra *extra; /* pointer to additional data */ } ExplainState; /* Hook for plugins to get control in ExplainOneQuery() */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index c5071c87b73..929a728106d 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -660,8 +660,9 @@ extern Datum pg_get_function_result(PG_FUNCTION_ARGS); extern char *deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit); extern List *deparse_context_for(const char *aliasname, Oid relid); -extern List *deparse_context_for_planstate(Node *planstate, List *ancestors, - List *rtable, List *rtable_names); +extern List *deparse_context_for_plan_rtable(List *rtable, List *rtable_names); +extern List *set_deparse_context_planstate(List *dpcontext, + Node *planstate, List *ancestors); extern List *select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used); extern const char *quote_identifier(const char *ident);