@@ -82,6 +82,15 @@ static void show_qual(List *qual, const char *qlabel,
82
82
static void show_scan_qual (List * qual , const char * qlabel ,
83
83
PlanState * planstate , List * ancestors ,
84
84
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 );
85
94
static void show_upper_qual (List * qual , const char * qlabel ,
86
95
PlanState * planstate , List * ancestors ,
87
96
ExplainState * es );
@@ -197,6 +206,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
197
206
es -> settings = defGetBoolean (opt );
198
207
else if (strcmp (opt -> defname , "generic_plan" ) == 0 )
199
208
es -> generic = defGetBoolean (opt );
209
+ else if (strcmp (opt -> defname , "stats" ) == 0 )
210
+ es -> stats = defGetBoolean (opt );
200
211
else if (strcmp (opt -> defname , "timing" ) == 0 )
201
212
{
202
213
timing_set = true;
@@ -471,6 +482,11 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
471
482
472
483
if (es -> buffers )
473
484
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
+
474
490
INSTR_TIME_SET_CURRENT (planstart );
475
491
476
492
/* plan the query */
@@ -2096,6 +2112,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
2096
2112
if (plan -> qual )
2097
2113
show_instrumentation_count ("Rows Removed by Filter" , 1 ,
2098
2114
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 );
2099
2120
break ;
2100
2121
case T_IndexOnlyScan :
2101
2122
show_scan_qual (((IndexOnlyScan * ) plan )-> indexqual ,
@@ -2112,10 +2133,20 @@ ExplainNode(PlanState *planstate, List *ancestors,
2112
2133
if (es -> analyze )
2113
2134
ExplainPropertyFloat ("Heap Fetches" , NULL ,
2114
2135
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 );
2115
2141
break ;
2116
2142
case T_BitmapIndexScan :
2117
2143
show_scan_qual (((BitmapIndexScan * ) plan )-> indexqualorig ,
2118
2144
"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 );
2119
2150
break ;
2120
2151
case T_BitmapHeapScan :
2121
2152
show_scan_qual (((BitmapHeapScan * ) plan )-> bitmapqualorig ,
@@ -2146,6 +2177,12 @@ ExplainNode(PlanState *planstate, List *ancestors,
2146
2177
planstate , es );
2147
2178
if (IsA (plan , CteScan ))
2148
2179
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 );
2149
2186
break ;
2150
2187
case T_Gather :
2151
2188
{
@@ -2213,6 +2250,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
2213
2250
if (plan -> qual )
2214
2251
show_instrumentation_count ("Rows Removed by Filter" , 1 ,
2215
2252
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 );
2216
2258
break ;
2217
2259
case T_TableFuncScan :
2218
2260
if (es -> verbose )
@@ -2327,6 +2369,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
2327
2369
if (plan -> qual )
2328
2370
show_instrumentation_count ("Rows Removed by Filter" , 1 ,
2329
2371
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 );
2330
2377
break ;
2331
2378
case T_WindowAgg :
2332
2379
show_upper_qual (plan -> qual , "Filter" , planstate , ancestors , es );
@@ -2343,6 +2390,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
2343
2390
if (plan -> qual )
2344
2391
show_instrumentation_count ("Rows Removed by Filter" , 1 ,
2345
2392
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 );
2346
2398
break ;
2347
2399
case T_Sort :
2348
2400
show_sort_keys (castNode (SortState , planstate ), ancestors , es );
@@ -2669,6 +2721,94 @@ show_scan_qual(List *qual, const char *qlabel,
2669
2721
show_qual (qual , qlabel , planstate , ancestors , useprefix , es );
2670
2722
}
2671
2723
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
+
2672
2812
/*
2673
2813
* Show a qualifier expression for an upper-level plan node
2674
2814
*/
0 commit comments