Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a5cd70d

Browse files
committed
Improve performance of EXPLAIN with large range tables.
As of 9.3, ruleutils.c goes to some lengths to ensure that table and column aliases used in its output are unique. Of course this takes more time than was required before, which in itself isn't fatal. However, EXPLAIN was set up so that recalculation of the unique aliases was repeated for each subexpression printed in a plan. That results in O(N^2) time and memory consumption for large plan trees, which did not happen in older branches. Fortunately, the expensive work is the same across a whole plan tree, so there is no need to repeat it; we can do most of the initialization just once per query and re-use it for each subexpression. This buys back most (not all) of the performance loss since 9.2. We need an extra ExplainState field to hold the precalculated deparse context. That's no problem in HEAD, but in the back branches, expanding sizeof(ExplainState) seems risky because third-party extensions might have local variables of that struct type. So, in 9.4 and 9.3, introduce an auxiliary struct to keep sizeof(ExplainState) the same. We should refactor the APIs to avoid such local variables in future, but that's material for a separate HEAD-only commit. Per gripe from Alexey Bashtanov. Back-patch to 9.3 where the issue was introduced.
1 parent 0b49642 commit a5cd70d

File tree

4 files changed

+66
-38
lines changed

4 files changed

+66
-38
lines changed

src/backend/commands/explain.c

+11-12
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,8 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
563563
es->rtable = queryDesc->plannedstmt->rtable;
564564
ExplainPreScanNode(queryDesc->planstate, &rels_used);
565565
es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
566+
es->deparse_cxt = deparse_context_for_plan_rtable(es->rtable,
567+
es->rtable_names);
566568
ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
567569
}
568570

@@ -1678,10 +1680,9 @@ show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
16781680
return;
16791681

16801682
/* Set up deparsing context */
1681-
context = deparse_context_for_planstate((Node *) planstate,
1682-
ancestors,
1683-
es->rtable,
1684-
es->rtable_names);
1683+
context = set_deparse_context_planstate(es->deparse_cxt,
1684+
(Node *) planstate,
1685+
ancestors);
16851686
useprefix = list_length(es->rtable) > 1;
16861687

16871688
/* Deparse each result column (we now include resjunk ones) */
@@ -1710,10 +1711,9 @@ show_expression(Node *node, const char *qlabel,
17101711
char *exprstr;
17111712

17121713
/* Set up deparsing context */
1713-
context = deparse_context_for_planstate((Node *) planstate,
1714-
ancestors,
1715-
es->rtable,
1716-
es->rtable_names);
1714+
context = set_deparse_context_planstate(es->deparse_cxt,
1715+
(Node *) planstate,
1716+
ancestors);
17171717

17181718
/* Deparse the expression */
17191719
exprstr = deparse_expression(node, context, useprefix, false);
@@ -1855,10 +1855,9 @@ show_sort_group_keys(PlanState *planstate, const char *qlabel,
18551855
return;
18561856

18571857
/* Set up deparsing context */
1858-
context = deparse_context_for_planstate((Node *) planstate,
1859-
ancestors,
1860-
es->rtable,
1861-
es->rtable_names);
1858+
context = set_deparse_context_planstate(es->deparse_cxt,
1859+
(Node *) planstate,
1860+
ancestors);
18621861
useprefix = (list_length(es->rtable) > 1 || es->verbose);
18631862

18641863
for (keyno = 0; keyno < nkeys; keyno++)

src/backend/utils/adt/ruleutils.c

+50-23
Original file line numberDiff line numberDiff line change
@@ -2520,7 +2520,43 @@ deparse_context_for(const char *aliasname, Oid relid)
25202520
}
25212521

25222522
/*
2523-
* deparse_context_for_planstate - Build deparse context for a plan
2523+
* deparse_context_for_plan_rtable - Build deparse context for a plan's rtable
2524+
*
2525+
* When deparsing an expression in a Plan tree, we use the plan's rangetable
2526+
* to resolve names of simple Vars. The initialization of column names for
2527+
* this is rather expensive if the rangetable is large, and it'll be the same
2528+
* for every expression in the Plan tree; so we do it just once and re-use
2529+
* the result of this function for each expression. (Note that the result
2530+
* is not usable until set_deparse_context_planstate() is applied to it.)
2531+
*
2532+
* In addition to the plan's rangetable list, pass the per-RTE alias names
2533+
* assigned by a previous call to select_rtable_names_for_explain.
2534+
*/
2535+
List *
2536+
deparse_context_for_plan_rtable(List *rtable, List *rtable_names)
2537+
{
2538+
deparse_namespace *dpns;
2539+
2540+
dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
2541+
2542+
/* Initialize fields that stay the same across the whole plan tree */
2543+
dpns->rtable = rtable;
2544+
dpns->rtable_names = rtable_names;
2545+
dpns->ctes = NIL;
2546+
2547+
/*
2548+
* Set up column name aliases. We will get rather bogus results for join
2549+
* RTEs, but that doesn't matter because plan trees don't contain any join
2550+
* alias Vars.
2551+
*/
2552+
set_simple_column_names(dpns);
2553+
2554+
/* Return a one-deep namespace stack */
2555+
return list_make1(dpns);
2556+
}
2557+
2558+
/*
2559+
* set_deparse_context_planstate - Specify Plan node containing expression
25242560
*
25252561
* When deparsing an expression in a Plan tree, we might have to resolve
25262562
* 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)
25332569
* fields, which won't contain INDEX_VAR Vars.)
25342570
*
25352571
* Note: planstate really ought to be declared as "PlanState *", but we use
2536-
* "Node *" to avoid having to include execnodes.h in builtins.h.
2572+
* "Node *" to avoid having to include execnodes.h in ruleutils.h.
25372573
*
25382574
* The ancestors list is a list of the PlanState's parent PlanStates, the
25392575
* most-closely-nested first. This is needed to resolve PARAM_EXEC Params.
25402576
* Note we assume that all the PlanStates share the same rtable.
25412577
*
2542-
* The plan's rangetable list must also be passed, along with the per-RTE
2543-
* alias names assigned by a previous call to select_rtable_names_for_explain.
2544-
* (We use the rangetable to resolve simple Vars, but the plan inputs are
2545-
* necessary for Vars with special varnos.)
2578+
* Once this function has been called, deparse_expression() can be called on
2579+
* subsidiary expression(s) of the specified PlanState node. To deparse
2580+
* expressions of a different Plan node in the same Plan tree, re-call this
2581+
* function to identify the new parent Plan node.
2582+
*
2583+
* The result is the same List passed in; this is a notational convenience.
25462584
*/
25472585
List *
2548-
deparse_context_for_planstate(Node *planstate, List *ancestors,
2549-
List *rtable, List *rtable_names)
2586+
set_deparse_context_planstate(List *dpcontext,
2587+
Node *planstate, List *ancestors)
25502588
{
25512589
deparse_namespace *dpns;
25522590

2553-
dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
2554-
2555-
/* Initialize fields that stay the same across the whole plan tree */
2556-
dpns->rtable = rtable;
2557-
dpns->rtable_names = rtable_names;
2558-
dpns->ctes = NIL;
2559-
2560-
/*
2561-
* Set up column name aliases. We will get rather bogus results for join
2562-
* RTEs, but that doesn't matter because plan trees don't contain any join
2563-
* alias Vars.
2564-
*/
2565-
set_simple_column_names(dpns);
2591+
/* Should always have one-entry namespace list for Plan deparsing */
2592+
Assert(list_length(dpcontext) == 1);
2593+
dpns = (deparse_namespace *) linitial(dpcontext);
25662594

25672595
/* Set our attention on the specific plan node passed in */
25682596
set_deparse_planstate(dpns, (PlanState *) planstate);
25692597
dpns->ancestors = ancestors;
25702598

2571-
/* Return a one-deep namespace stack */
2572-
return list_make1(dpns);
2599+
return dpcontext;
25732600
}
25742601

25752602
/*

src/include/commands/explain.h

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ typedef struct ExplainState
4141
List *rtable_names; /* alias names for RTEs */
4242
int indent; /* current indentation level */
4343
List *grouping_stack; /* format-specific grouping state */
44+
List *deparse_cxt; /* context list for deparsing expressions */
4445
} ExplainState;
4546

4647
/* Hook for plugins to get control in ExplainOneQuery() */

src/include/utils/ruleutils.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
9-
* src/include/ruleutils.h
9+
* src/include/utils/ruleutils.h
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -25,8 +25,9 @@ extern char *pg_get_constraintdef_string(Oid constraintId);
2525
extern char *deparse_expression(Node *expr, List *dpcontext,
2626
bool forceprefix, bool showimplicit);
2727
extern List *deparse_context_for(const char *aliasname, Oid relid);
28-
extern List *deparse_context_for_planstate(Node *planstate, List *ancestors,
29-
List *rtable, List *rtable_names);
28+
extern List *deparse_context_for_plan_rtable(List *rtable, List *rtable_names);
29+
extern List *set_deparse_context_planstate(List *dpcontext,
30+
Node *planstate, List *ancestors);
3031
extern List *select_rtable_names_for_explain(List *rtable,
3132
Bitmapset *rels_used);
3233
extern char *generate_collation_name(Oid collid);

0 commit comments

Comments
 (0)