diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 8a0be5df0bfc932cfec25eefc3debf9f637b9914..39ceaf266391801421caaf2ad19dae5c66113519 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -563,6 +563,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); } @@ -1678,10 +1680,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) */ @@ -1710,10 +1711,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); @@ -1855,10 +1855,9 @@ show_sort_group_keys(PlanState *planstate, const char *qlabel, 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 dd748acffdf73f85e8521bd923447bf95f4b7c76..c1d860ceffd14c623007b4d6243b73dcb83aad7b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2520,7 +2520,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 @@ -2533,43 +2569,34 @@ deparse_context_for(const char *aliasname, Oid relid) * fields, which won't contain INDEX_VAR Vars.) * * Note: planstate really ought to be declared as "PlanState *", but we use - * "Node *" to avoid having to include execnodes.h in builtins.h. + * "Node *" to avoid having to include execnodes.h in ruleutils.h. * * The ancestors list is a list of the PlanState's parent PlanStates, the * 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 6e269501f48ca8e45d778c35d4daa63efc1ded47..598f37428d26d67fed7bb708afb981ab683e282c 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -41,6 +41,7 @@ typedef struct ExplainState List *rtable_names; /* alias names for RTEs */ int indent; /* current indentation level */ List *grouping_stack; /* format-specific grouping state */ + List *deparse_cxt; /* context list for deparsing expressions */ } ExplainState; /* Hook for plugins to get control in ExplainOneQuery() */ diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h index 188503caa044fd0904d93defbfc2b52d7d4eb697..fed9c7b6ffaf8ae1aeb8d21e1b6e12f351111ed8 100644 --- a/src/include/utils/ruleutils.h +++ b/src/include/utils/ruleutils.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * src/include/ruleutils.h + * src/include/utils/ruleutils.h * *------------------------------------------------------------------------- */ @@ -25,8 +25,9 @@ extern char *pg_get_constraintdef_string(Oid constraintId); 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 char *generate_collation_name(Oid collid);