Skip to content

Commit c981970

Browse files
author
Commitfest Bot
committed
[CF 5359] Showing applied extended statistics in explain Part2
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5359 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CAOKkKFtaCPamg-GRtUjADGbzXCL0exhkK5FiznkJB-8-dANf1Q@mail.gmail.com Author(s): Tatsuro Yamada
2 parents e033696 + 829c212 commit c981970

File tree

19 files changed

+365
-2
lines changed

19 files changed

+365
-2
lines changed

contrib/auto_explain/auto_explain.c

+13
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ static bool auto_explain_log_analyze = false;
3030
static bool auto_explain_log_verbose = false;
3131
static bool auto_explain_log_buffers = false;
3232
static bool auto_explain_log_wal = false;
33+
static bool auto_explain_log_stats = false;
3334
static bool auto_explain_log_triggers = false;
3435
static bool auto_explain_log_timing = true;
3536
static bool auto_explain_log_settings = false;
@@ -171,6 +172,17 @@ _PG_init(void)
171172
NULL,
172173
NULL);
173174

175+
DefineCustomBoolVariable("auto_explain.log_stats",
176+
"Use EXPLAIN STATS for plan logging.",
177+
NULL,
178+
&auto_explain_log_stats,
179+
false,
180+
PGC_SUSET,
181+
0,
182+
NULL,
183+
NULL,
184+
NULL);
185+
174186
DefineCustomBoolVariable("auto_explain.log_triggers",
175187
"Include trigger statistics in plans.",
176188
"This has no effect unless log_analyze is also set.",
@@ -405,6 +417,7 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
405417
es->summary = es->analyze;
406418
/* No support for MEMORY */
407419
/* es->memory = false; */
420+
es->stats = auto_explain_log_stats;
408421
es->format = auto_explain_log_format;
409422
es->settings = auto_explain_log_settings;
410423

doc/src/sgml/auto-explain.sgml

+18
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,24 @@ LOAD 'auto_explain';
148148
</listitem>
149149
</varlistentry>
150150

151+
<varlistentry id="auto-explain-configuration-parameters-log-stats">
152+
<term>
153+
<varname>auto_explain.log_stats</varname> (<type>boolean</type>)
154+
<indexterm>
155+
<primary><varname>auto_explain.log_stats</varname> configuration parameter</primary>
156+
</indexterm>
157+
</term>
158+
<listitem>
159+
<para>
160+
<varname>auto_explain.log_stats</varname> controls whether applied
161+
Extended Statistic are printed when an execution plan is logged; it's
162+
equivalent to the <literal>STATS</literal> option of <command>EXPLAIN</command>.
163+
This parameter is off by default.
164+
Only superusers can change this setting.
165+
</para>
166+
</listitem>
167+
</varlistentry>
168+
151169
<varlistentry id="auto-explain-configuration-parameters-log-timing">
152170
<term>
153171
<varname>auto_explain.log_timing</varname> (<type>boolean</type>)

doc/src/sgml/ref/explain.sgml

+14
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ EXPLAIN [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] <rep
4343
BUFFERS [ <replaceable class="parameter">boolean</replaceable> ]
4444
SERIALIZE [ { NONE | TEXT | BINARY } ]
4545
WAL [ <replaceable class="parameter">boolean</replaceable> ]
46+
STATS [ <replaceable class="parameter">boolean</replaceable> ]
4647
TIMING [ <replaceable class="parameter">boolean</replaceable> ]
4748
SUMMARY [ <replaceable class="parameter">boolean</replaceable> ]
4849
MEMORY [ <replaceable class="parameter">boolean</replaceable> ]
@@ -251,6 +252,19 @@ ROLLBACK;
251252
</listitem>
252253
</varlistentry>
253254

255+
<varlistentry>
256+
<term><literal>STATS</literal></term>
257+
<listitem>
258+
<para>
259+
Include information on applied <literal>Extended statistics</literal>.
260+
Specifically, include the names of extended statistics and clauses.
261+
Supported extended statistics types are Dependencies and MCV.
262+
See <xref linkend="planner-stats-extended"/> for details about extended
263+
statistics. This parameter defaults to <literal>FALSE</literal>.
264+
</para>
265+
</listitem>
266+
</varlistentry>
267+
254268
<varlistentry>
255269
<term><literal>TIMING</literal></term>
256270
<listitem>

src/backend/commands/explain.c

+140
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ static void show_qual(List *qual, const char *qlabel,
8282
static void show_scan_qual(List *qual, const char *qlabel,
8383
PlanState *planstate, List *ancestors,
8484
ExplainState *es);
85+
static char *deparse_stat_expression(Node *node,
86+
PlanState *planstate, List *ancestors,
87+
bool useprefix, ExplainState *es);
88+
static char *show_stat_qual(List *qual, int is_or,
89+
PlanState *planstate, List *ancestors,
90+
bool useprefix, ExplainState *es);
91+
static void show_scan_stats(List *stats, List *clauses, List *ors,
92+
PlanState *planstate, List *ancestors,
93+
ExplainState *es);
8594
static void show_upper_qual(List *qual, const char *qlabel,
8695
PlanState *planstate, List *ancestors,
8796
ExplainState *es);
@@ -197,6 +206,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
197206
es->settings = defGetBoolean(opt);
198207
else if (strcmp(opt->defname, "generic_plan") == 0)
199208
es->generic = defGetBoolean(opt);
209+
else if (strcmp(opt->defname, "stats") == 0)
210+
es->stats = defGetBoolean(opt);
200211
else if (strcmp(opt->defname, "timing") == 0)
201212
{
202213
timing_set = true;
@@ -471,6 +482,11 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
471482

472483
if (es->buffers)
473484
bufusage_start = pgBufferUsage;
485+
486+
/* if this flag is true, applied ext stats are stored */
487+
if (es->stats)
488+
query->isExplain_Stats = true;
489+
474490
INSTR_TIME_SET_CURRENT(planstart);
475491

476492
/* plan the query */
@@ -2096,6 +2112,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
20962112
if (plan->qual)
20972113
show_instrumentation_count("Rows Removed by Filter", 1,
20982114
planstate, es);
2115+
if (es->stats)
2116+
show_scan_stats(plan->app_extstats->applied_stats,
2117+
plan->app_extstats->applied_clauses,
2118+
plan->app_extstats->applied_clauses_or,
2119+
planstate, ancestors, es);
20992120
break;
21002121
case T_IndexOnlyScan:
21012122
show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
@@ -2112,10 +2133,20 @@ ExplainNode(PlanState *planstate, List *ancestors,
21122133
if (es->analyze)
21132134
ExplainPropertyFloat("Heap Fetches", NULL,
21142135
planstate->instrument->ntuples2, 0, es);
2136+
if (es->stats)
2137+
show_scan_stats(plan->app_extstats->applied_stats,
2138+
plan->app_extstats->applied_clauses,
2139+
plan->app_extstats->applied_clauses_or,
2140+
planstate, ancestors, es);
21152141
break;
21162142
case T_BitmapIndexScan:
21172143
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
21182144
"Index Cond", planstate, ancestors, es);
2145+
if (es->stats)
2146+
show_scan_stats(plan->app_extstats->applied_stats,
2147+
plan->app_extstats->applied_clauses,
2148+
plan->app_extstats->applied_clauses_or,
2149+
planstate, ancestors, es);
21192150
break;
21202151
case T_BitmapHeapScan:
21212152
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
@@ -2146,6 +2177,12 @@ ExplainNode(PlanState *planstate, List *ancestors,
21462177
planstate, es);
21472178
if (IsA(plan, CteScan))
21482179
show_ctescan_info(castNode(CteScanState, planstate), es);
2180+
2181+
if (es->stats)
2182+
show_scan_stats(plan->app_extstats->applied_stats,
2183+
plan->app_extstats->applied_clauses,
2184+
plan->app_extstats->applied_clauses_or,
2185+
planstate, ancestors, es);
21492186
break;
21502187
case T_Gather:
21512188
{
@@ -2213,6 +2250,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
22132250
if (plan->qual)
22142251
show_instrumentation_count("Rows Removed by Filter", 1,
22152252
planstate, es);
2253+
if (es->stats)
2254+
show_scan_stats(plan->app_extstats->applied_stats,
2255+
plan->app_extstats->applied_clauses,
2256+
plan->app_extstats->applied_clauses_or,
2257+
planstate, ancestors, es);
22162258
break;
22172259
case T_TableFuncScan:
22182260
if (es->verbose)
@@ -2327,6 +2369,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
23272369
if (plan->qual)
23282370
show_instrumentation_count("Rows Removed by Filter", 1,
23292371
planstate, es);
2372+
if (es->stats)
2373+
show_scan_stats(plan->app_extstats->applied_stats,
2374+
plan->app_extstats->applied_clauses,
2375+
plan->app_extstats->applied_clauses_or,
2376+
planstate, ancestors, es);
23302377
break;
23312378
case T_WindowAgg:
23322379
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
@@ -2343,6 +2390,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
23432390
if (plan->qual)
23442391
show_instrumentation_count("Rows Removed by Filter", 1,
23452392
planstate, es);
2393+
if (es->stats)
2394+
show_scan_stats(plan->app_extstats->applied_stats,
2395+
plan->app_extstats->applied_clauses,
2396+
plan->app_extstats->applied_clauses_or,
2397+
planstate, ancestors, es);
23462398
break;
23472399
case T_Sort:
23482400
show_sort_keys(castNode(SortState, planstate), ancestors, es);
@@ -2669,6 +2721,94 @@ show_scan_qual(List *qual, const char *qlabel,
26692721
show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
26702722
}
26712723

2724+
/*
2725+
* Show a generic expression
2726+
*/
2727+
static char *
2728+
deparse_stat_expression(Node *node,
2729+
PlanState *planstate, List *ancestors,
2730+
bool useprefix, ExplainState *es)
2731+
{
2732+
List *context;
2733+
2734+
/* Set up deparsing context */
2735+
context = set_deparse_context_plan(es->deparse_cxt,
2736+
planstate->plan,
2737+
ancestors);
2738+
2739+
/* Deparse the expression */
2740+
return deparse_expression(node, context, useprefix, false);
2741+
}
2742+
2743+
/*
2744+
* Show a qualifier expression for extended stats
2745+
*/
2746+
static char *
2747+
show_stat_qual(List *qual, int is_or,
2748+
PlanState *planstate, List *ancestors,
2749+
bool useprefix, ExplainState *es)
2750+
{
2751+
Node *node;
2752+
2753+
/* No work if empty qual */
2754+
if (qual == NIL)
2755+
return NULL;
2756+
2757+
/* Convert AND list to explicit AND */
2758+
switch (is_or)
2759+
{
2760+
case 0:
2761+
node = (Node *) make_ands_explicit(qual);
2762+
break;
2763+
case 1:
2764+
node = (Node *) make_ors_explicit(qual);
2765+
break;
2766+
case 2:
2767+
/* Extended stats for GROUP BY clause should be comma separeted string */
2768+
node = (Node *) qual;
2769+
break;
2770+
default:
2771+
elog(ERROR, "unexpected value: %d", is_or);
2772+
break;
2773+
}
2774+
2775+
/* And show it */
2776+
return deparse_stat_expression(node, planstate, ancestors, useprefix, es);
2777+
}
2778+
2779+
/*
2780+
* Show applied statistics for scan/agg/group plan node
2781+
*/
2782+
static void
2783+
show_scan_stats(List *stats, List *clauses, List *ors,
2784+
PlanState *planstate, List *ancestors, ExplainState *es)
2785+
{
2786+
ListCell *lc1, *lc2, *lc3;
2787+
StringInfoData str;
2788+
bool useprefix;
2789+
2790+
useprefix = es->verbose;
2791+
2792+
forthree (lc1, stats, lc2, clauses, lc3, ors)
2793+
{
2794+
StatisticExtInfo *stat = (StatisticExtInfo *) lfirst(lc1);
2795+
List *applied_clauses = (List *) lfirst(lc2);
2796+
int is_or = lfirst_int(lc3);
2797+
2798+
initStringInfo(&str);
2799+
2800+
if (useprefix)
2801+
appendStringInfo(&str, "%s.",
2802+
get_namespace_name(get_statistics_namespace(stat->statOid)));
2803+
2804+
appendStringInfo(&str, "%s Clauses: %s",
2805+
get_statistics_name(stat->statOid),
2806+
show_stat_qual(applied_clauses, is_or, planstate, ancestors, useprefix, es));
2807+
2808+
ExplainPropertyText("Ext Stats", str.data, es);
2809+
}
2810+
}
2811+
26722812
/*
26732813
* Show a qualifier expression for an upper-level plan node
26742814
*/

src/backend/nodes/makefuncs.c

+11
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,17 @@ make_ands_explicit(List *andclauses)
759759
return make_andclause(andclauses);
760760
}
761761

762+
Expr *
763+
make_ors_explicit(List *orclauses)
764+
{
765+
if (orclauses == NIL)
766+
return (Expr *) makeBoolConst(true, false);
767+
else if (list_length(orclauses) == 1)
768+
return (Expr *) linitial(orclauses);
769+
else
770+
return make_orclause(orclauses);
771+
}
772+
762773
List *
763774
make_ands_implicit(Expr *clause)
764775
{

src/backend/optimizer/plan/createplan.c

+17
Original file line numberDiff line numberDiff line change
@@ -5455,13 +5455,30 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
54555455
static void
54565456
copy_generic_path_info(Plan *dest, Path *src)
54575457
{
5458+
ListCell *lc;
5459+
54585460
dest->disabled_nodes = src->disabled_nodes;
54595461
dest->startup_cost = src->startup_cost;
54605462
dest->total_cost = src->total_cost;
54615463
dest->plan_rows = src->rows;
54625464
dest->plan_width = src->pathtarget->width;
54635465
dest->parallel_aware = src->parallel_aware;
54645466
dest->parallel_safe = src->parallel_safe;
5467+
5468+
/* Is this the right place to use makeNode()? */
5469+
dest->app_extstats = makeNode(Applied_ExtStats);
5470+
dest->app_extstats->applied_stats = src->parent->applied_stats;
5471+
dest->app_extstats->applied_clauses_or = src->parent->applied_clauses_or;
5472+
dest->app_extstats->applied_clauses = NIL;
5473+
5474+
foreach (lc, src->parent->applied_clauses)
5475+
{
5476+
List *clauses = (List *) lfirst(lc);
5477+
5478+
dest->app_extstats->applied_clauses
5479+
= lappend(dest->app_extstats->applied_clauses,
5480+
maybe_extract_actual_clauses(clauses, false));
5481+
}
54655482
}
54665483

54675484
/*

src/backend/optimizer/util/relnode.c

+12
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
287287
rel->partexprs = NULL;
288288
rel->nullable_partexprs = NULL;
289289

290+
rel->applied_stats = NIL;
291+
rel->applied_clauses = NIL;
292+
rel->applied_clauses_or = NIL;
293+
290294
/*
291295
* Pass assorted information down the inheritance hierarchy.
292296
*/
@@ -769,6 +773,10 @@ build_join_rel(PlannerInfo *root,
769773
joinrel->partexprs = NULL;
770774
joinrel->nullable_partexprs = NULL;
771775

776+
joinrel->applied_stats = NIL;
777+
joinrel->applied_clauses = NIL;
778+
joinrel->applied_clauses_or = NIL;
779+
772780
/* Compute information relevant to the foreign relations. */
773781
set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
774782

@@ -953,6 +961,10 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
953961
joinrel->partexprs = NULL;
954962
joinrel->nullable_partexprs = NULL;
955963

964+
joinrel->applied_stats = NIL;
965+
joinrel->applied_clauses = NIL;
966+
joinrel->applied_clauses_or = NIL;
967+
956968
/* Compute information relevant to foreign relations. */
957969
set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
958970

0 commit comments

Comments
 (0)