Hello Ekaterina,
seems like an interesting and useful improvement. I did a quick review
of the patch - attached is a 0002 patch with a couple minor changes (the
0001 is just your v1 patch, to keep cfbot happy).
1) There's a couple changes to follow project code style (e.g. brackets
after "if" on a separate line, no brackets around single-line blocks,
etc.). I've reverted some unnecessary whitespace changes. Minor stuff.
2) I don't think InstrEndLoop needs to check if min_t == 0 before
initializing it in the first loop. It certainly has to be 0, no? Same
for min_tuples. I also suggest comment explaining that we don't have to
initialize the max values.
3) In ExplainNode, in the part processing per-worker stats, I think some
of the fields are incorrectly referencing planstate->instrument instead
of using the 'instrument' variable from WorkerInstrumentation.
In general, I agree with Andres this might add overhead to explain
analyze, although I doubt it's going to be measurable. But maybe try
doing some measurements for common and worst-cases.
I wonder if we should have another option EXPLAIN option enabling this.
I.e. by default we'd not collect/print this, and users would have to
pass some option to EXPLAIN. Or maybe we could tie this to VERBOSE?
Also, why print this only for nested loop, and not for all nodes with
(nloops > 1)? I see there was some discussion why checking nodeTag is
necessary to identify NL, but that's not my point.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>From 903c203e7e5f298f927ade97ca03a0e129c31e75 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <to...@2ndquadrant.com>
Date: Sat, 31 Oct 2020 01:47:46 +0100
Subject: [PATCH 1/2] extra_statistics_v1
---
src/backend/commands/explain.c | 131 ++++++++++++++----
src/backend/executor/instrument.c | 32 ++++-
src/include/executor/instrument.h | 4 +
src/test/regress/expected/partition_prune.out | 36 ++---
src/test/regress/expected/select_parallel.out | 12 +-
src/test/regress/sql/partition_prune.sql | 2 +
6 files changed, 163 insertions(+), 54 deletions(-)
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 43f9b01e83..72dae57ee2 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1568,29 +1568,64 @@ ExplainNode(PlanState *planstate, List *ancestors,
double startup_ms = 1000.0 *
planstate->instrument->startup / nloops;
double total_ms = 1000.0 *
planstate->instrument->total / nloops;
double rows = planstate->instrument->ntuples / nloops;
+ double min_r = planstate->instrument->min_tuples;
+ double max_r = planstate->instrument->max_tuples;
+ double min_t_ms = 1000.0 *
planstate->instrument->min_t;
+ double max_t_ms = 1000.0 *
planstate->instrument->max_t;
if (es->format == EXPLAIN_FORMAT_TEXT)
{
- if (es->timing)
- appendStringInfo(es->str,
- " (actual
time=%.3f..%.3f rows=%.0f loops=%.0f)",
- startup_ms,
total_ms, rows, nloops);
+ if (nodeTag(plan) == T_NestLoop) {
+ if (es->timing)
+ appendStringInfo(es->str,
+ "
(actual time=%.3f..%.3f min_time=%.3f max_time=%.3f min_rows=%.0f rows=%.0f
max_rows=%.0f loops=%.0f)",
+
startup_ms, total_ms, min_t_ms, max_t_ms, min_r, rows, max_r, nloops);
+ else
+ appendStringInfo(es->str,
+ "
(actual min_rows=%.0f rows=%.0f max_rows=%.0f loops=%.0f)",
+ min_r,
rows, max_r, nloops);
+ }
else
- appendStringInfo(es->str,
- " (actual
rows=%.0f loops=%.0f)",
- rows, nloops);
+ {
+ if (es->timing)
+ appendStringInfo(es->str,
+ "
(actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
+
startup_ms, total_ms, rows, nloops);
+ else
+ appendStringInfo(es->str,
+ "
(actual rows=%.0f loops=%.0f)",
+ rows,
nloops);
+ }
}
else
{
- if (es->timing)
+ if (nodeTag(plan) == T_NestLoop) {
+ if (es->timing) {
+ ExplainPropertyFloat("Actual Startup
Time", "s", startup_ms,
+
3, es);
+ ExplainPropertyFloat("Actual Total
Time", "s", total_ms,
+
3, es);
+ ExplainPropertyFloat("Min Time", "s",
min_t_ms,
+
3, es);
+ ExplainPropertyFloat("Max Time", "s",
max_t_ms,
+
3, es);
+ }
+ ExplainPropertyFloat("Actual Rows", NULL, rows,
0, es);
+ ExplainPropertyFloat("Min Rows", NULL, rows, 0,
es);
+ ExplainPropertyFloat("Max Rows", NULL, rows, 0,
es);
+ ExplainPropertyFloat("Actual Loops", NULL,
nloops, 0, es);
+ }
+ else
{
- ExplainPropertyFloat("Actual Startup Time",
"s", startup_ms,
- 3, es);
- ExplainPropertyFloat("Actual Total Time", "s",
total_ms,
- 3, es);
+ if (es->timing) {
+ ExplainPropertyFloat("Actual Startup
Time", "s", startup_ms,
+
3, es);
+ ExplainPropertyFloat("Actual Total
Time", "s", total_ms,
+
3, es);
+ }
+ ExplainPropertyFloat("Actual Rows", NULL, rows,
0, es);
+ ExplainPropertyFloat("Actual Loops", NULL,
nloops, 0, es);
}
- ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
- ExplainPropertyFloat("Actual Loops", NULL, nloops, 0,
es);
}
}
else if (es->analyze)
@@ -1599,6 +1634,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
appendStringInfoString(es->str, " (never executed)");
else
{
+ /* without min and max values because actual result is
0 */
if (es->timing)
{
ExplainPropertyFloat("Actual Startup Time",
"ms", 0.0, 3, es);
@@ -1624,39 +1660,78 @@ ExplainNode(PlanState *planstate, List *ancestors,
double nloops = instrument->nloops;
double startup_ms;
double total_ms;
+ double min_t_ms;
+ double max_t_ms;
double rows;
+ double min_r;
+ double max_r;
if (nloops <= 0)
continue;
startup_ms = 1000.0 * instrument->startup / nloops;
total_ms = 1000.0 * instrument->total / nloops;
+ min_t_ms = 1000.0 * planstate->instrument->min_t;
+ max_t_ms = 1000.0 * planstate->instrument->max_t;
rows = instrument->ntuples / nloops;
+ min_r = planstate->instrument->min_tuples;
+ max_r = planstate->instrument->max_tuples;
ExplainOpenWorker(n, es);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
ExplainIndentText(es);
- if (es->timing)
- appendStringInfo(es->str,
-
"actual time=%.3f..%.3f rows=%.0f loops=%.0f\n",
-
startup_ms, total_ms, rows, nloops);
+ if (nodeTag(plan) == T_NestLoop) {
+ if (es->timing)
+ appendStringInfo(es->str,
+
"actual time=%.3f..%.3f min_time=%.3f max_time=%.3f min_rows=%.0f rows=%.0f
max_rows=%.0f loops=%.0f\n",
+
startup_ms, total_ms, min_t_ms, max_t_ms, min_r, rows, max_r, nloops);
+ else
+ appendStringInfo(es->str,
+
"actual min_rows=%.0f rows=%.0f max_rows=%.0f loops=%.0f\n",
+
min_r, rows, max_r, nloops);
+ }
else
- appendStringInfo(es->str,
-
"actual rows=%.0f loops=%.0f\n",
- rows,
nloops);
+ {
+ if (es->timing)
+ appendStringInfo(es->str,
+
"actual time=%.3f..%.3f rows=%.0f loops=%.0f\n",
+
startup_ms, total_ms, rows, nloops);
+ else
+ appendStringInfo(es->str,
+
"actual rows=%.0f loops=%.0f\n",
+
rows, nloops);
+ }
}
else
{
- if (es->timing)
+ if (nodeTag(plan) == T_NestLoop) {
+ if (es->timing) {
+ ExplainPropertyFloat("Actual
Startup Time", "ms",
+
startup_ms, 3, es);
+ ExplainPropertyFloat("Actual
Total Time", "ms",
+
total_ms, 3, es);
+ ExplainPropertyFloat("Min
Time", "ms",
+
min_t_ms, 3, es);
+ ExplainPropertyFloat("Max
Time", "ms",
+
max_t_ms, 3, es);
+ }
+ ExplainPropertyFloat("Actual Rows",
NULL, rows, 0, es);
+ ExplainPropertyFloat("Min Rows", NULL,
rows, 0, es);
+ ExplainPropertyFloat("Max Rows", NULL,
rows, 0, es);
+ ExplainPropertyFloat("Actual Loops",
NULL, nloops, 0, es);
+ }
+ else
{
- ExplainPropertyFloat("Actual Startup
Time", "ms",
-
startup_ms, 3, es);
- ExplainPropertyFloat("Actual Total
Time", "ms",
-
total_ms, 3, es);
+ if (es->timing) {
+ ExplainPropertyFloat("Actual
Startup Time", "ms",
+
startup_ms, 3, es);
+ ExplainPropertyFloat("Actual
Total Time", "ms",
+
total_ms, 3, es);
+ }
+ ExplainPropertyFloat("Actual Rows",
NULL, rows, 0, es);
+ ExplainPropertyFloat("Actual Loops",
NULL, nloops, 0, es);
}
- ExplainPropertyFloat("Actual Rows", NULL, rows,
0, es);
- ExplainPropertyFloat("Actual Loops", NULL,
nloops, 0, es);
}
ExplainCloseWorker(n, es);
diff --git a/src/backend/executor/instrument.c
b/src/backend/executor/instrument.c
index fbedb5aaf6..b0748721c3 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -118,9 +118,8 @@ InstrStopNode(Instrumentation *instr, double nTuples)
/* Finish a run cycle for a plan node */
void
-InstrEndLoop(Instrumentation *instr)
-{
- double totaltime;
+InstrEndLoop(Instrumentation *instr) {
+ double totaltime;
/* Skip if nothing has happened, or already shut down */
if (!instr->running)
@@ -134,7 +133,34 @@ InstrEndLoop(Instrumentation *instr)
instr->startup += instr->firsttuple;
instr->total += totaltime;
+ if (instr->nloops == 0) /* this is
first loop */
+ {
+ if (instr->min_t == 0)
+ {
+ instr->min_t = totaltime; /* init min_t */
+ }
+ if (instr->min_tuples == 0)
+ {
+ instr->min_tuples = instr->tuplecount; /* init
min_tuples */
+ }
+ }
+ if (instr->min_t > totaltime)
+ {
+ instr->min_t = totaltime;
+ }
+ if (instr->max_t < totaltime)
+ {
+ instr->max_t = totaltime;
+ }
instr->ntuples += instr->tuplecount;
+ if (instr->min_tuples > instr->tuplecount)
+ {
+ instr->min_tuples = instr->tuplecount;
+ }
+ if (instr->max_tuples < instr->tuplecount)
+ {
+ instr->max_tuples = instr->tuplecount;
+ }
instr->nloops += 1;
/* Reset for next cycle (if any) */
diff --git a/src/include/executor/instrument.h
b/src/include/executor/instrument.h
index 9dc3ecb07d..dbfac349cc 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -66,7 +66,11 @@ typedef struct Instrumentation
/* Accumulated statistics across all completed cycles: */
double startup; /* total startup time (in
seconds) */
double total; /* total time (in seconds) */
+ double min_t; /* time of fastest loop (in
seconds) */
+ double max_t; /* time of slowest loop (in
seconds) */
double ntuples; /* total tuples produced */
+ double min_tuples; /* min counter of produced
tuples for all loops */
+ double max_tuples; /* max counter of produced
tuples for all loops */
double ntuples2; /* secondary node-specific
tuple counter */
double nloops; /* # of run cycles for this
node */
double nfiltered1; /* # of tuples removed by
scanqual or joinqual */
diff --git a/src/test/regress/expected/partition_prune.out
b/src/test/regress/expected/partition_prune.out
index 50d2a7e4b9..ba864a5767 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -1929,6 +1929,8 @@ begin
loop
ln := regexp_replace(ln, 'Workers Launched: \d+', 'Workers Launched:
N');
ln := regexp_replace(ln, 'actual rows=\d+ loops=\d+', 'actual rows=N
loops=N');
+ ln := regexp_replace(ln, 'actual min_rows=\d+ rows=\d+ max_rows=\d+
loops=\d+',
+ 'actual min_rows=N rows=N max_rows=N loops=N');
ln := regexp_replace(ln, 'Rows Removed by Filter: \d+', 'Rows Removed
by Filter: N');
return next ln;
end loop;
@@ -2065,7 +2067,7 @@ select explain_parallel_append('select avg(ab.a) from ab
inner join lprt_a a on
Workers Planned: 1
Workers Launched: N
-> Partial Aggregate (actual rows=N loops=N)
- -> Nested Loop (actual rows=N loops=N)
+ -> Nested Loop (actual min_rows=N rows=N max_rows=N loops=N)
-> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
Filter: (a = ANY ('{0,0,1}'::integer[]))
-> Append (actual rows=N loops=N)
@@ -2099,7 +2101,7 @@ select explain_parallel_append('select avg(ab.a) from ab
inner join lprt_a a on
Workers Planned: 1
Workers Launched: N
-> Partial Aggregate (actual rows=N loops=N)
- -> Nested Loop (actual rows=N loops=N)
+ -> Nested Loop (actual min_rows=N rows=N max_rows=N loops=N)
-> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
Filter: (a = ANY ('{0,0,1}'::integer[]))
-> Append (actual rows=N loops=N)
@@ -2132,7 +2134,7 @@ select explain_parallel_append('select avg(ab.a) from ab
inner join lprt_a a on
Workers Planned: 1
Workers Launched: N
-> Partial Aggregate (actual rows=N loops=N)
- -> Nested Loop (actual rows=N loops=N)
+ -> Nested Loop (actual min_rows=N rows=N max_rows=N loops=N)
-> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
Filter: (a = ANY ('{1,0,3}'::integer[]))
-> Append (actual rows=N loops=N)
@@ -2164,7 +2166,7 @@ select explain_parallel_append('select avg(ab.a) from ab
inner join lprt_a a on
Workers Planned: 1
Workers Launched: N
-> Partial Aggregate (actual rows=N loops=N)
- -> Nested Loop (actual rows=N loops=N)
+ -> Nested Loop (actual min_rows=N rows=N max_rows=N loops=N)
-> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
Filter: (a = ANY ('{1,0,0}'::integer[]))
Rows Removed by Filter: N
@@ -2198,7 +2200,7 @@ select explain_parallel_append('select avg(ab.a) from ab
inner join lprt_a a on
Workers Planned: 1
Workers Launched: N
-> Partial Aggregate (actual rows=N loops=N)
- -> Nested Loop (actual rows=N loops=N)
+ -> Nested Loop (actual min_rows=N rows=N max_rows=N loops=N)
-> Parallel Seq Scan on lprt_a a (actual rows=N loops=N)
Filter: (a = ANY ('{1,0,0}'::integer[]))
Rows Removed by Filter: N
@@ -2441,7 +2443,7 @@ update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a =
ab_a1.a;
Update on ab_a1_b1 ab_a1_1
Update on ab_a1_b2 ab_a1_2
Update on ab_a1_b3 ab_a1_3
- -> Nested Loop (actual rows=0 loops=1)
+ -> Nested Loop (actual min_rows=0 rows=0 max_rows=0 loops=1)
-> Append (actual rows=1 loops=1)
-> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
Recheck Cond: (a = 1)
@@ -2461,7 +2463,7 @@ update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a =
ab_a1.a;
Recheck Cond: (a = 1)
-> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0
loops=1)
Index Cond: (a = 1)
- -> Nested Loop (actual rows=1 loops=1)
+ -> Nested Loop (actual min_rows=1 rows=1 max_rows=1 loops=1)
-> Append (actual rows=1 loops=1)
-> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
Recheck Cond: (a = 1)
@@ -2482,7 +2484,7 @@ update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a =
ab_a1.a;
Heap Blocks: exact=1
-> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1
loops=1)
Index Cond: (a = 1)
- -> Nested Loop (actual rows=0 loops=1)
+ -> Nested Loop (actual min_rows=0 rows=0 max_rows=0 loops=1)
-> Append (actual rows=1 loops=1)
-> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
Recheck Cond: (a = 1)
@@ -2523,7 +2525,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select
1);
Update on ab_a1_b3 ab_a1_3
InitPlan 1 (returns $0)
-> Result (actual rows=1 loops=1)
- -> Nested Loop (actual rows=1 loops=1)
+ -> Nested Loop (actual min_rows=1 rows=1 max_rows=1 loops=1)
-> Seq Scan on ab_a1_b1 ab_a1_1 (actual rows=1 loops=1)
-> Materialize (actual rows=1 loops=1)
-> Append (actual rows=1 loops=1)
@@ -2533,7 +2535,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select
1);
Filter: (b = $0)
-> Seq Scan on ab_a2_b3 ab_a2_3 (never executed)
Filter: (b = $0)
- -> Nested Loop (actual rows=1 loops=1)
+ -> Nested Loop (actual min_rows=1 rows=1 max_rows=1 loops=1)
-> Seq Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1)
-> Materialize (actual rows=1 loops=1)
-> Append (actual rows=1 loops=1)
@@ -2543,7 +2545,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select
1);
Filter: (b = $0)
-> Seq Scan on ab_a2_b3 ab_a2_3 (never executed)
Filter: (b = $0)
- -> Nested Loop (actual rows=1 loops=1)
+ -> Nested Loop (actual min_rows=1 rows=1 max_rows=1 loops=1)
-> Seq Scan on ab_a1_b3 ab_a1_3 (actual rows=1 loops=1)
-> Materialize (actual rows=1 loops=1)
-> Append (actual rows=1 loops=1)
@@ -2589,7 +2591,7 @@ explain (analyze, costs off, summary off, timing off)
select * from tbl1 join tprt on tbl1.col1 > tprt.col1;
QUERY PLAN
--------------------------------------------------------------------------
- Nested Loop (actual rows=6 loops=1)
+ Nested Loop (actual min_rows=6 rows=6 max_rows=6 loops=1)
-> Seq Scan on tbl1 (actual rows=2 loops=1)
-> Append (actual rows=3 loops=2)
-> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=2)
@@ -2610,7 +2612,7 @@ explain (analyze, costs off, summary off, timing off)
select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
QUERY PLAN
--------------------------------------------------------------------------
- Nested Loop (actual rows=2 loops=1)
+ Nested Loop (actual min_rows=2 rows=2 max_rows=2 loops=1)
-> Seq Scan on tbl1 (actual rows=2 loops=1)
-> Append (actual rows=1 loops=2)
-> Index Scan using tprt1_idx on tprt_1 (never executed)
@@ -2655,7 +2657,7 @@ explain (analyze, costs off, summary off, timing off)
select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1;
QUERY PLAN
--------------------------------------------------------------------------
- Nested Loop (actual rows=23 loops=1)
+ Nested Loop (actual min_rows=23 rows=23 max_rows=23 loops=1)
-> Seq Scan on tbl1 (actual rows=5 loops=1)
-> Append (actual rows=5 loops=5)
-> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=5)
@@ -2676,7 +2678,7 @@ explain (analyze, costs off, summary off, timing off)
select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1;
QUERY PLAN
--------------------------------------------------------------------------
- Nested Loop (actual rows=3 loops=1)
+ Nested Loop (actual min_rows=3 rows=3 max_rows=3 loops=1)
-> Seq Scan on tbl1 (actual rows=5 loops=1)
-> Append (actual rows=1 loops=5)
-> Index Scan using tprt1_idx on tprt_1 (never executed)
@@ -2740,7 +2742,7 @@ explain (analyze, costs off, summary off, timing off)
select * from tbl1 join tprt on tbl1.col1 < tprt.col1;
QUERY PLAN
--------------------------------------------------------------------------
- Nested Loop (actual rows=1 loops=1)
+ Nested Loop (actual min_rows=1 rows=1 max_rows=1 loops=1)
-> Seq Scan on tbl1 (actual rows=1 loops=1)
-> Append (actual rows=1 loops=1)
-> Index Scan using tprt1_idx on tprt_1 (never executed)
@@ -2772,7 +2774,7 @@ explain (analyze, costs off, summary off, timing off)
select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
QUERY PLAN
-------------------------------------------------------------------
- Nested Loop (actual rows=0 loops=1)
+ Nested Loop (actual min_rows=0 rows=0 max_rows=0 loops=1)
-> Seq Scan on tbl1 (actual rows=1 loops=1)
-> Append (actual rows=0 loops=1)
-> Index Scan using tprt1_idx on tprt_1 (never executed)
diff --git a/src/test/regress/expected/select_parallel.out
b/src/test/regress/expected/select_parallel.out
index 9b0c418db7..e8ccd903b4 100644
--- a/src/test/regress/expected/select_parallel.out
+++ b/src/test/regress/expected/select_parallel.out
@@ -547,10 +547,10 @@ alter table tenk2 set (parallel_workers = 0);
explain (analyze, timing off, summary off, costs off)
select count(*) from tenk1, tenk2 where tenk1.hundred > 1
and tenk2.thousand=0;
- QUERY PLAN
---------------------------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
- -> Nested Loop (actual rows=98000 loops=1)
+ -> Nested Loop (actual min_rows=98000 rows=98000 max_rows=98000 loops=1)
-> Seq Scan on tenk2 (actual rows=10 loops=1)
Filter: (thousand = 0)
Rows Removed by Filter: 9990
@@ -581,9 +581,9 @@ begin
end;
$$;
select * from explain_parallel_sort_stats();
- explain_parallel_sort_stats
---------------------------------------------------------------------------
- Nested Loop Left Join (actual rows=30000 loops=1)
+ explain_parallel_sort_stats
+---------------------------------------------------------------------------------
+ Nested Loop Left Join (actual min_rows=30000 rows=30000 max_rows=30000
loops=1)
-> Values Scan on "*VALUES*" (actual rows=3 loops=1)
-> Gather Merge (actual rows=10000 loops=3)
Workers Planned: 4
diff --git a/src/test/regress/sql/partition_prune.sql
b/src/test/regress/sql/partition_prune.sql
index 1e904a8c5b..2068856161 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -452,6 +452,8 @@ begin
loop
ln := regexp_replace(ln, 'Workers Launched: \d+', 'Workers Launched:
N');
ln := regexp_replace(ln, 'actual rows=\d+ loops=\d+', 'actual rows=N
loops=N');
+ ln := regexp_replace(ln, 'actual min_rows=\d+ rows=\d+ max_rows=\d+
loops=\d+',
+ 'actual min_rows=N rows=N max_rows=N loops=N');
ln := regexp_replace(ln, 'Rows Removed by Filter: \d+', 'Rows Removed
by Filter: N');
return next ln;
end loop;
--
2.26.2
>From 28f3bdd55cd8784d0af4d53a4e88845782e2eaa0 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <to...@2ndquadrant.com>
Date: Sat, 31 Oct 2020 02:07:20 +0100
Subject: [PATCH 2/2] minor tweaks
---
src/backend/commands/explain.c | 32 +++++++++++++++----------
src/backend/executor/instrument.c | 39 ++++++++++++++++---------------
2 files changed, 40 insertions(+), 31 deletions(-)
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 72dae57ee2..fa8deac55a 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1575,7 +1575,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
if (es->format == EXPLAIN_FORMAT_TEXT)
{
- if (nodeTag(plan) == T_NestLoop) {
+ if (nodeTag(plan) == T_NestLoop)
+ {
if (es->timing)
appendStringInfo(es->str,
"
(actual time=%.3f..%.3f min_time=%.3f max_time=%.3f min_rows=%.0f rows=%.0f
max_rows=%.0f loops=%.0f)",
@@ -1599,8 +1600,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
}
else
{
- if (nodeTag(plan) == T_NestLoop) {
- if (es->timing) {
+ if (nodeTag(plan) == T_NestLoop)
+ {
+ if (es->timing)
+ {
ExplainPropertyFloat("Actual Startup
Time", "s", startup_ms,
3, es);
ExplainPropertyFloat("Actual Total
Time", "s", total_ms,
@@ -1617,7 +1620,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
}
else
{
- if (es->timing) {
+ if (es->timing)
+ {
ExplainPropertyFloat("Actual Startup
Time", "s", startup_ms,
3, es);
ExplainPropertyFloat("Actual Total
Time", "s", total_ms,
@@ -1670,18 +1674,19 @@ ExplainNode(PlanState *planstate, List *ancestors,
continue;
startup_ms = 1000.0 * instrument->startup / nloops;
total_ms = 1000.0 * instrument->total / nloops;
- min_t_ms = 1000.0 * planstate->instrument->min_t;
- max_t_ms = 1000.0 * planstate->instrument->max_t;
+ min_t_ms = 1000.0 * instrument->min_t;
+ max_t_ms = 1000.0 * instrument->max_t;
rows = instrument->ntuples / nloops;
- min_r = planstate->instrument->min_tuples;
- max_r = planstate->instrument->max_tuples;
+ min_r = instrument->min_tuples;
+ max_r = instrument->max_tuples;
ExplainOpenWorker(n, es);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
ExplainIndentText(es);
- if (nodeTag(plan) == T_NestLoop) {
+ if (nodeTag(plan) == T_NestLoop)
+ {
if (es->timing)
appendStringInfo(es->str,
"actual time=%.3f..%.3f min_time=%.3f max_time=%.3f min_rows=%.0f rows=%.0f
max_rows=%.0f loops=%.0f\n",
@@ -1705,8 +1710,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
}
else
{
- if (nodeTag(plan) == T_NestLoop) {
- if (es->timing) {
+ if (nodeTag(plan) == T_NestLoop)
+ {
+ if (es->timing)
+ {
ExplainPropertyFloat("Actual
Startup Time", "ms",
startup_ms, 3, es);
ExplainPropertyFloat("Actual
Total Time", "ms",
@@ -1723,7 +1730,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
}
else
{
- if (es->timing) {
+ if (es->timing)
+ {
ExplainPropertyFloat("Actual
Startup Time", "ms",
startup_ms, 3, es);
ExplainPropertyFloat("Actual
Total Time", "ms",
diff --git a/src/backend/executor/instrument.c
b/src/backend/executor/instrument.c
index b0748721c3..0198bb3a5a 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -118,8 +118,9 @@ InstrStopNode(Instrumentation *instr, double nTuples)
/* Finish a run cycle for a plan node */
void
-InstrEndLoop(Instrumentation *instr) {
- double totaltime;
+InstrEndLoop(Instrumentation *instr)
+{
+ double totaltime;
/* Skip if nothing has happened, or already shut down */
if (!instr->running)
@@ -133,34 +134,34 @@ InstrEndLoop(Instrumentation *instr) {
instr->startup += instr->firsttuple;
instr->total += totaltime;
- if (instr->nloops == 0) /* this is
first loop */
+
+ /*
+ * this is first loop
+ *
+ * We only initialize the min values. We don't need to bother with
+ * the max, because those are 0 and the non-zero values will get
+ * updated a couple lines later.
+ */
+ if (instr->nloops == 0)
{
- if (instr->min_t == 0)
- {
- instr->min_t = totaltime; /* init min_t */
- }
- if (instr->min_tuples == 0)
- {
- instr->min_tuples = instr->tuplecount; /* init
min_tuples */
- }
+ instr->min_t = totaltime;
+ instr->min_tuples = instr->tuplecount;
}
+
if (instr->min_t > totaltime)
- {
instr->min_t = totaltime;
- }
+
if (instr->max_t < totaltime)
- {
instr->max_t = totaltime;
- }
+
instr->ntuples += instr->tuplecount;
+
if (instr->min_tuples > instr->tuplecount)
- {
instr->min_tuples = instr->tuplecount;
- }
+
if (instr->max_tuples < instr->tuplecount)
- {
instr->max_tuples = instr->tuplecount;
- }
+
instr->nloops += 1;
/* Reset for next cycle (if any) */
--
2.26.2