From 7871ac1afe7837a6dc0676a6c9819cc68a5c0f07 Mon Sep 17 00:00:00 2001
From: "e.sokolova" <e.sokolova@postgrespro.ru>
Date: Fri, 4 Sep 2020 18:00:47 +0300
Subject: Add min and max statistics without case of
 parallel workers. Tags: commitfest_hotfix.

---
 src/backend/commands/explain.c    | 19 +++++++++++++++----
 src/backend/executor/instrument.c | 13 +++++++++++++
 src/include/executor/instrument.h |  4 ++++
 3 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c98c9b5547c..a10a2cfdfee 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1571,17 +1571,21 @@ 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);
+								 " (actual time=%.3f..%.3f min time %.3f max time %.3f rows=%.0f min rows %.0f max rows %.0f loops=%.0f)",
+								 startup_ms, total_ms, min_t_ms, min_t_ms, rows, min_r, max_r, nloops);
 			else
 				appendStringInfo(es->str,
-								 " (actual rows=%.0f loops=%.0f)",
-								 rows, nloops);
+								 " (actual rows=%.0f min rows %.0f max rows %.0f loops=%.0f)",
+								 rows, min_r, max_r, nloops);
 		}
 		else
 		{
@@ -1589,10 +1593,16 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			{
 				ExplainPropertyFloat("Actual Startup Time", "s", startup_ms,
 									 3, es);
+                ExplainPropertyFloat("Min Time", "s", min_t_ms,
+                                     3, es);
 				ExplainPropertyFloat("Actual Total Time", "s", total_ms,
 									 3, es);
+                ExplainPropertyFloat("Max Time", "s", max_t_ms,
+                                     3, es);
 			}
+            ExplainPropertyFloat("Min Rows", NULL, rows, 0, es);
 			ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
+            ExplainPropertyFloat("Max Rows", NULL, rows, 0, es);
 			ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
 		}
 	}
@@ -1602,6 +1612,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);
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index fbedb5aaf60..a46201c9284 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -134,7 +134,20 @@ InstrEndLoop(Instrumentation *instr)
 
 	instr->startup += instr->firsttuple;
 	instr->total += totaltime;
+	if (instr->nloops == 0)                     // this is first loop
+    {
+        instr->min_t = totaltime;               // init min_t
+        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 9dc3ecb07d7..eb95bbcbd9a 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 */
-- 
2.26.0


From ebdfe117e4074d268e3e7c480b98d375d1d6f62b Mon Sep 17 00:00:00 2001
From: "e.sokolova" <e.sokolova@postgrespro.ru>
Date: Fri, 11 Sep 2020 23:04:34 +0300
Subject: Add case of parallel workers. Tags:
 commitfest_hotfix.

---
 src/backend/commands/explain.c    | 36 +++++++++++++++++++++----------
 src/backend/executor/instrument.c |  6 +++---
 2 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a10a2cfdfee..b60d17542d5 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1580,12 +1580,12 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		{
 			if (es->timing)
 				appendStringInfo(es->str,
-								 " (actual time=%.3f..%.3f min time %.3f max time %.3f rows=%.0f min rows %.0f max rows %.0f loops=%.0f)",
-								 startup_ms, total_ms, min_t_ms, min_t_ms, rows, min_r, max_r, nloops);
+								 " (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 rows=%.0f min rows %.0f max rows %.0f loops=%.0f)",
-								 rows, min_r, max_r, nloops);
+								 " (actual min_rows=%.0f rows=%.0f max_rows=%.0f loops=%.0f)",
+                                 min_r, rows, max_r, nloops);
 		}
 		else
 		{
@@ -1593,10 +1593,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			{
 				ExplainPropertyFloat("Actual Startup Time", "s", startup_ms,
 									 3, es);
-                ExplainPropertyFloat("Min Time", "s", min_t_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);
 			}
@@ -1612,7 +1612,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			appendStringInfoString(es->str, " (never executed)");
 		else
 		{
-		    // without min and max values because actual result is 0
+		    /* without min and max values because actual result is 0 */
 			if (es->timing)
 			{
 				ExplainPropertyFloat("Actual Startup Time", "ms", 0.0, 3, es);
@@ -1638,13 +1638,21 @@ 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);
 
@@ -1653,12 +1661,12 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				ExplainIndentText(es);
 				if (es->timing)
 					appendStringInfo(es->str,
-									 "actual time=%.3f..%.3f rows=%.0f loops=%.0f\n",
-									 startup_ms, total_ms, rows, nloops);
+									 "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 rows=%.0f loops=%.0f\n",
-									 rows, nloops);
+									 "actual min_rows=%.0f rows=%.0f max_rows=%.0f loops=%.0f\n",
+                                     min_r, rows, max_r, nloops);
 			}
 			else
 			{
@@ -1668,8 +1676,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
 										 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("Min Rows", NULL, rows, 0, es);
 				ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
+                ExplainPropertyFloat("Max Rows", NULL, rows, 0, es);
 				ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
 			}
 
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index a46201c9284..30ccc876e7c 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -134,10 +134,10 @@ InstrEndLoop(Instrumentation *instr)
 
 	instr->startup += instr->firsttuple;
 	instr->total += totaltime;
-	if (instr->nloops == 0)                     // this is first loop
+	if (instr->nloops == 0)                       /* this is first loop */
     {
-        instr->min_t = totaltime;               // init min_t
-        instr->min_tuples = instr->tuplecount;  // init min_tuples
+        instr->min_t = totaltime;                 /* init min_t */
+        instr->min_tuples = instr->tuplecount;    /* init min_tuples */
     }
 	if (instr->min_t > totaltime)
 	    instr->min_t = totaltime;
-- 
2.26.0


From ecbf04d519e17b8968103364e89169ab965b41d7 Mon Sep 17 00:00:00 2001
From: "e.sokolova" <e.sokolova@postgrespro.ru>
Date: Fri, 18 Sep 2020 13:35:19 +0300
Subject: Fix bugs. Tags: commitfest_hotfix.

---
 src/backend/commands/explain.c    | 142 ++++++++++++++++++++----------
 src/backend/executor/instrument.c |  16 ++--
 2 files changed, 105 insertions(+), 53 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index b60d17542d5..d700a4168c7 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1578,32 +1578,57 @@ ExplainNode(PlanState *planstate, List *ancestors,
 
 		if (es->format == EXPLAIN_FORMAT_TEXT)
 		{
-			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);
+            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
+            {
+                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)
-			{
-				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("Min Rows", NULL, rows, 0, es);
-			ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
-            ExplainPropertyFloat("Max Rows", NULL, rows, 0, es);
-			ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
+            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
+            {
+                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);
+            }
 		}
 	}
 	else if (es->analyze)
@@ -1659,32 +1684,57 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			if (es->format == EXPLAIN_FORMAT_TEXT)
 			{
 				ExplainIndentText(es);
-				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);
+                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
+                {
+                    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)
-				{
-					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("Min Rows", NULL, rows, 0, es);
-				ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
-                ExplainPropertyFloat("Max Rows", NULL, rows, 0, es);
-				ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
+                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
+                {
+                    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);
+                }
 			}
 
 			ExplainCloseWorker(n, es);
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index 30ccc876e7c..79ea3cf94ba 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -134,15 +134,17 @@ InstrEndLoop(Instrumentation *instr)
 
 	instr->startup += instr->firsttuple;
 	instr->total += totaltime;
-	if (instr->nloops == 0)                       /* this is first loop */
+	if (instr->nloops == 0)                                /* this is first loop */
     {
-        instr->min_t = totaltime;                 /* init min_t */
-        instr->min_tuples = instr->tuplecount;    /* init min_tuples */
+	    if (instr->min_t == 0)
+            instr->min_t = totaltime - instr->firsttuple;  /* 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;
+	if (instr->min_t > totaltime - instr->firsttuple)
+	    instr->min_t = totaltime - instr->firsttuple;
+	if (instr->max_t < totaltime - instr->firsttuple)
+        instr->max_t = totaltime - instr->firsttuple;
 	instr->ntuples += instr->tuplecount;
     if (instr->min_tuples > instr->tuplecount)
         instr->min_tuples = instr->tuplecount;
-- 
2.26.0


From 7566a98bbc33a24052e1334b0afe2cf341c0818f Mon Sep 17 00:00:00 2001
From: "e.sokolova" <e.sokolova@postgrespro.ru>
Date: Fri, 25 Sep 2020 20:09:22 +0300
Subject: Fix tests. Tags: commitfest_hotfix.

---
 src/backend/executor/instrument.c             | 31 +++++++++++------
 src/test/regress/expected/partition_prune.out | 34 +++++++++----------
 src/test/regress/expected/select_parallel.out | 12 +++----
 3 files changed, 44 insertions(+), 33 deletions(-)

diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index 79ea3cf94ba..42b2caade01 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)
@@ -136,20 +135,32 @@ InstrEndLoop(Instrumentation *instr)
 	instr->total += totaltime;
 	if (instr->nloops == 0)                                /* this is first loop */
     {
-	    if (instr->min_t == 0)
+        if (instr->min_t == 0)
+        {
             instr->min_t = totaltime - instr->firsttuple;  /* init min_t */
+        }
         if (instr->min_tuples == 0)
+        {
             instr->min_tuples = instr->tuplecount;         /* init min_tuples */
-    }
+        }
+	}
 	if (instr->min_t > totaltime - instr->firsttuple)
+	{
 	    instr->min_t = totaltime - instr->firsttuple;
+	}
 	if (instr->max_t < totaltime - instr->firsttuple)
-        instr->max_t = totaltime - instr->firsttuple;
+	{
+		instr->max_t = totaltime - instr->firsttuple;
+	}
 	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;
+	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/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 50d2a7e4b97..db0b167ef4a 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2065,7 +2065,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=0 rows=0 max_rows=0 loops=2)
                      ->  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 +2099,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=0 rows=0 max_rows=0 loops=2)
                      ->  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 +2132,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=0 rows=0 max_rows=0 loops=2)
                      ->  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 +2164,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=0 rows=0 max_rows=0 loops=2)
                      ->  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 +2198,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=0 rows=0 max_rows=0 loops=2)
                      ->  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 +2441,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 +2461,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 +2482,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 +2523,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 +2533,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 +2543,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 +2589,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 +2610,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 +2655,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 +2676,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 +2740,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 +2772,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 96dfb7c8dd6..65559b2261e 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
-- 
2.26.0

