From 7fd8280b72bc0f252c7a0718f241bddbef65ec68 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Mon, 24 Feb 2025 10:57:11 -0500
Subject: [PATCH] EXPLAIN: Always use two fractional digits for row counts.

Commit ddb17e387aa28d61521227377b00f997756b8a27 attempted to avoid
confusing users by displaying digits after the decimal point only when
nloops > 1, since it's impossible to have a fraction row count after a
single iteration. However, this made the regression tests unstable since
parallal queries will have nloops>1 for all nodes below the Gather or
Gather Merge in normal cases, but if the workers don't start in time and
the leader finishes all the work, they will suddenly have nloops==1,
making it unpredictable whether the digits after the decimal point would
be displayed or not.

Various fixes are possible here. For example, it has previously been
proposed that we should try to display the digits after the decimal
point only if rows/nloops is an integer, but rows is a float so it's not
theoretically an exact quantity -- precision could be lost in extreme
cases. It has also been proposed that we should try to display the
digits after the decimal point only if we're under some sort of
construct that could potentially cause looping regardless of whether it
actually does. While such ideas are not without merit, this patch adopts
the much simpler solution of always display two decimal digits. If that
approach stands up to scrutiny from the buildfarm and human users, it
spares us the trouble of doing anything more complex; if not, we can
reassess.

This commit incidentally reverts 44cbba9a7f51a3888d5087fc94b23614ba2b81f2,
which should no longer be needed.
---
 .../expected/level_tracking.out               |  24 +-
 .../postgres_fdw/expected/postgres_fdw.out    |  60 +--
 src/backend/commands/explain.c                |  34 +-
 src/test/regress/expected/brin_multi.out      |  72 +--
 src/test/regress/expected/explain.out         |  89 ++--
 src/test/regress/expected/gin.out             |  20 +-
 .../regress/expected/incremental_sort.out     |  20 +-
 src/test/regress/expected/matview.out         |   8 +-
 src/test/regress/expected/memoize.out         |  94 ++--
 src/test/regress/expected/merge.out           | 124 ++---
 src/test/regress/expected/misc_functions.out  |  60 +--
 src/test/regress/expected/partition_prune.out | 480 +++++++++---------
 src/test/regress/expected/select.out          |   6 +-
 src/test/regress/expected/select_into.out     |  16 +-
 src/test/regress/expected/select_parallel.out |  12 +-
 src/test/regress/expected/subselect.out       |  12 +-
 src/test/regress/expected/tidscan.out         |  16 +-
 src/test/regress/sql/explain.sql              |   3 -
 18 files changed, 562 insertions(+), 588 deletions(-)

diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index 297ebc5159e..03bea14d5da 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -904,16 +904,16 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
 (1 row)
 
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT 100;
-           QUERY PLAN           
---------------------------------
- Result (actual rows=1 loops=1)
+            QUERY PLAN             
+-----------------------------------
+ Result (actual rows=1.00 loops=1)
 (1 row)
 
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
   DECLARE foocur CURSOR FOR SELECT * FROM stats_track_tab;
-                     QUERY PLAN                      
------------------------------------------------------
- Seq Scan on stats_track_tab (actual rows=0 loops=1)
+                       QUERY PLAN                       
+--------------------------------------------------------
+ Seq Scan on stats_track_tab (actual rows=0.00 loops=1)
 (1 row)
 
 SELECT toplevel, calls, query FROM pg_stat_statements
@@ -937,16 +937,16 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
 (1 row)
 
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT 100;
-           QUERY PLAN           
---------------------------------
- Result (actual rows=1 loops=1)
+            QUERY PLAN             
+-----------------------------------
+ Result (actual rows=1.00 loops=1)
 (1 row)
 
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
   DECLARE foocur CURSOR FOR SELECT * FROM stats_track_tab;
-                     QUERY PLAN                      
------------------------------------------------------
- Seq Scan on stats_track_tab (actual rows=0 loops=1)
+                       QUERY PLAN                       
+--------------------------------------------------------
+ Seq Scan on stats_track_tab (actual rows=0.00 loops=1)
 (1 row)
 
 SELECT toplevel, calls, query FROM pg_stat_statements
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index daa3b1d7a6d..8447b289cb7 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -11670,15 +11670,15 @@ SELECT * FROM local_tbl, async_pt WHERE local_tbl.a = async_pt.a AND local_tbl.c
 
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
 SELECT * FROM local_tbl, async_pt WHERE local_tbl.a = async_pt.a AND local_tbl.c = 'bar';
-                                  QUERY PLAN                                   
--------------------------------------------------------------------------------
- Nested Loop (actual rows=1 loops=1)
-   ->  Seq Scan on local_tbl (actual rows=1 loops=1)
+                                    QUERY PLAN                                    
+----------------------------------------------------------------------------------
+ Nested Loop (actual rows=1.00 loops=1)
+   ->  Seq Scan on local_tbl (actual rows=1.00 loops=1)
          Filter: (c = 'bar'::text)
          Rows Removed by Filter: 1
-   ->  Append (actual rows=1 loops=1)
+   ->  Append (actual rows=1.00 loops=1)
          ->  Async Foreign Scan on async_p1 async_pt_1 (never executed)
-         ->  Async Foreign Scan on async_p2 async_pt_2 (actual rows=1 loops=1)
+         ->  Async Foreign Scan on async_p2 async_pt_2 (actual rows=1.00 loops=1)
          ->  Seq Scan on async_p3 async_pt_3 (never executed)
                Filter: (a = local_tbl.a)
 (9 rows)
@@ -11916,20 +11916,20 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W
 
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
 SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt WHERE a < 3000) FROM async_pt WHERE a < 3000) t2 ON t1.a = t2.a;
-                                       QUERY PLAN                                        
------------------------------------------------------------------------------------------
- Nested Loop Left Join (actual rows=1 loops=1)
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Nested Loop Left Join (actual rows=1.00 loops=1)
    Join Filter: (t1.a = async_pt.a)
    Rows Removed by Join Filter: 399
    InitPlan 1
-     ->  Aggregate (actual rows=1 loops=1)
-           ->  Append (actual rows=400 loops=1)
-                 ->  Async Foreign Scan on async_p1 async_pt_4 (actual rows=200 loops=1)
-                 ->  Async Foreign Scan on async_p2 async_pt_5 (actual rows=200 loops=1)
-   ->  Seq Scan on local_tbl t1 (actual rows=1 loops=1)
-   ->  Append (actual rows=400 loops=1)
-         ->  Async Foreign Scan on async_p1 async_pt_1 (actual rows=200 loops=1)
-         ->  Async Foreign Scan on async_p2 async_pt_2 (actual rows=200 loops=1)
+     ->  Aggregate (actual rows=1.00 loops=1)
+           ->  Append (actual rows=400.00 loops=1)
+                 ->  Async Foreign Scan on async_p1 async_pt_4 (actual rows=200.00 loops=1)
+                 ->  Async Foreign Scan on async_p2 async_pt_5 (actual rows=200.00 loops=1)
+   ->  Seq Scan on local_tbl t1 (actual rows=1.00 loops=1)
+   ->  Append (actual rows=400.00 loops=1)
+         ->  Async Foreign Scan on async_p1 async_pt_1 (actual rows=200.00 loops=1)
+         ->  Async Foreign Scan on async_p2 async_pt_2 (actual rows=200.00 loops=1)
 (12 rows)
 
 SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt WHERE a < 3000) FROM async_pt WHERE a < 3000) t2 ON t1.a = t2.a;
@@ -11960,15 +11960,15 @@ SELECT * FROM async_pt t1 WHERE t1.b === 505 LIMIT 1;
 
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
 SELECT * FROM async_pt t1 WHERE t1.b === 505 LIMIT 1;
-                               QUERY PLAN                                
--------------------------------------------------------------------------
- Limit (actual rows=1 loops=1)
-   ->  Append (actual rows=1 loops=1)
-         ->  Async Foreign Scan on async_p1 t1_1 (actual rows=0 loops=1)
+                                 QUERY PLAN                                 
+----------------------------------------------------------------------------
+ Limit (actual rows=1.00 loops=1)
+   ->  Append (actual rows=1.00 loops=1)
+         ->  Async Foreign Scan on async_p1 t1_1 (actual rows=0.00 loops=1)
                Filter: (b === 505)
-         ->  Async Foreign Scan on async_p2 t1_2 (actual rows=0 loops=1)
+         ->  Async Foreign Scan on async_p2 t1_2 (actual rows=0.00 loops=1)
                Filter: (b === 505)
-         ->  Seq Scan on async_p3 t1_3 (actual rows=1 loops=1)
+         ->  Seq Scan on async_p3 t1_3 (actual rows=1.00 loops=1)
                Filter: (b === 505)
                Rows Removed by Filter: 101
 (9 rows)
@@ -12120,12 +12120,12 @@ DELETE FROM async_p2;
 DELETE FROM async_p3;
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
 SELECT * FROM async_pt;
-                               QUERY PLAN                                
--------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
-   ->  Async Foreign Scan on async_p1 async_pt_1 (actual rows=0 loops=1)
-   ->  Async Foreign Scan on async_p2 async_pt_2 (actual rows=0 loops=1)
-   ->  Seq Scan on async_p3 async_pt_3 (actual rows=0 loops=1)
+                                 QUERY PLAN                                 
+----------------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
+   ->  Async Foreign Scan on async_p1 async_pt_1 (actual rows=0.00 loops=1)
+   ->  Async Foreign Scan on async_p2 async_pt_2 (actual rows=0.00 loops=1)
+   ->  Seq Scan on async_p3 async_pt_3 (actual rows=0.00 loops=1)
 (4 rows)
 
 -- Clean up
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c0d614866a9..4271dd48e4e 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1998,10 +1998,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			if (es->timing)
 				appendStringInfo(es->str, "time=%.3f..%.3f ", startup_ms, total_ms);
 
-			if (nloops > 1)
-				appendStringInfo(es->str, "rows=%.2f loops=%.0f)", rows, nloops);
-			else
-				appendStringInfo(es->str, "rows=%.0f loops=%.0f)", rows, nloops);
+			appendStringInfo(es->str, "rows=%.2f loops=%.0f)", rows, nloops);
 		}
 		else
 		{
@@ -2012,16 +2009,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				ExplainPropertyFloat("Actual Total Time", "ms", total_ms,
 									 3, es);
 			}
-			if (nloops > 1)
-			{
-				ExplainPropertyFloat("Actual Rows", NULL, rows, 2, es);
-				ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
-			}
-			else
-			{
-				ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
-				ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
-			}
+			ExplainPropertyFloat("Actual Rows", NULL, rows, 2, es);
+			ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
 		}
 	}
 	else if (es->analyze)
@@ -2077,10 +2066,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				if (es->timing)
 					appendStringInfo(es->str, "time=%.3f..%.3f", startup_ms, total_ms);
 
-				if (nloops > 1)
-					appendStringInfo(es->str, "rows=%.2f loops=%.0f\n", rows, nloops);
-				else
-					appendStringInfo(es->str, "rows=%.0f loops=%.0f\n", rows, nloops);
+				appendStringInfo(es->str, "rows=%.2f loops=%.0f\n", rows, nloops);
 			}
 			else
 			{
@@ -2092,16 +2078,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 										 total_ms, 3, es);
 				}
 
-				if (nloops > 1)
-				{
-					ExplainPropertyFloat("Actual Rows", NULL, rows, 2, es);
-					ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
-				}
-				else
-				{
-					ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
-					ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
-				}
+				ExplainPropertyFloat("Actual Rows", NULL, rows, 2, es);
+				ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
 			}
 
 			ExplainCloseWorker(n, es);
diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out
index f2d14658181..991b7eacada 100644
--- a/src/test/regress/expected/brin_multi.out
+++ b/src/test/regress/expected/brin_multi.out
@@ -847,11 +847,11 @@ SET enable_seqscan = off;
 -- make sure the ranges were built correctly and 2023-01-01 eliminates all
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date;
-                               QUERY PLAN                                
--------------------------------------------------------------------------
- Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1)
+                                 QUERY PLAN                                 
+----------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_date_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '2023-01-01'::date)
-   ->  Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_date_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '2023-01-01'::date)
 (4 rows)
 
@@ -866,21 +866,21 @@ CREATE INDEX ON brin_timestamp_test USING brin (a timestamp_minmax_multi_ops) WI
 SET enable_seqscan = off;
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_timestamp_test WHERE a = '2023-01-01'::timestamp;
-                                  QUERY PLAN                                  
-------------------------------------------------------------------------------
- Bitmap Heap Scan on brin_timestamp_test (actual rows=0 loops=1)
+                                   QUERY PLAN                                    
+---------------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_timestamp_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '2023-01-01 00:00:00'::timestamp without time zone)
-   ->  Bitmap Index Scan on brin_timestamp_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_timestamp_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '2023-01-01 00:00:00'::timestamp without time zone)
 (4 rows)
 
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_timestamp_test WHERE a = '1900-01-01'::timestamp;
-                                  QUERY PLAN                                  
-------------------------------------------------------------------------------
- Bitmap Heap Scan on brin_timestamp_test (actual rows=0 loops=1)
+                                   QUERY PLAN                                    
+---------------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_timestamp_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '1900-01-01 00:00:00'::timestamp without time zone)
-   ->  Bitmap Index Scan on brin_timestamp_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_timestamp_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '1900-01-01 00:00:00'::timestamp without time zone)
 (4 rows)
 
@@ -894,21 +894,21 @@ CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_
 SET enable_seqscan = off;
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date;
-                               QUERY PLAN                                
--------------------------------------------------------------------------
- Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1)
+                                 QUERY PLAN                                 
+----------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_date_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '2023-01-01'::date)
-   ->  Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_date_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '2023-01-01'::date)
 (4 rows)
 
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_date_test WHERE a = '1900-01-01'::date;
-                               QUERY PLAN                                
--------------------------------------------------------------------------
- Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1)
+                                 QUERY PLAN                                 
+----------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_date_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '1900-01-01'::date)
-   ->  Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_date_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '1900-01-01'::date)
 (4 rows)
 
@@ -923,21 +923,21 @@ CREATE INDEX ON brin_interval_test USING brin (a interval_minmax_multi_ops) WITH
 SET enable_seqscan = off;
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_interval_test WHERE a = '-30 years'::interval;
-                                 QUERY PLAN                                  
------------------------------------------------------------------------------
- Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1)
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_interval_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '@ 30 years ago'::interval)
-   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '@ 30 years ago'::interval)
 (4 rows)
 
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_interval_test WHERE a = '30 years'::interval;
-                                 QUERY PLAN                                  
------------------------------------------------------------------------------
- Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1)
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_interval_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '@ 30 years'::interval)
-   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '@ 30 years'::interval)
 (4 rows)
 
@@ -951,21 +951,21 @@ CREATE INDEX ON brin_interval_test USING brin (a interval_minmax_multi_ops) WITH
 SET enable_seqscan = off;
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_interval_test WHERE a = '-30 years'::interval;
-                                 QUERY PLAN                                  
------------------------------------------------------------------------------
- Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1)
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_interval_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '@ 30 years ago'::interval)
-   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '@ 30 years ago'::interval)
 (4 rows)
 
 EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF, BUFFERS OFF)
 SELECT * FROM brin_interval_test WHERE a = '30 years'::interval;
-                                 QUERY PLAN                                  
------------------------------------------------------------------------------
- Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1)
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_interval_test (actual rows=0.00 loops=1)
    Recheck Cond: (a = '@ 30 years'::interval)
-   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1)
+   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0.00 loops=1)
          Index Cond: (a = '@ 30 years'::interval)
 (4 rows)
 
diff --git a/src/test/regress/expected/explain.out b/src/test/regress/expected/explain.out
index 97eb4b76526..f5d60e50893 100644
--- a/src/test/regress/expected/explain.out
+++ b/src/test/regress/expected/explain.out
@@ -61,26 +61,26 @@ select explain_filter('explain select * from int8_tbl i8');
 (1 row)
 
 select explain_filter('explain (analyze, buffers off) select * from int8_tbl i8');
-                                        explain_filter                                         
------------------------------------------------------------------------------------------------
- Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N)
+                                         explain_filter                                          
+-------------------------------------------------------------------------------------------------
+ Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
  Planning Time: N.N ms
  Execution Time: N.N ms
 (3 rows)
 
 select explain_filter('explain (analyze, buffers off, verbose) select * from int8_tbl i8');
-                                            explain_filter                                            
-------------------------------------------------------------------------------------------------------
- Seq Scan on public.int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N)
+                                             explain_filter                                             
+--------------------------------------------------------------------------------------------------------
+ Seq Scan on public.int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
    Output: q1, q2
  Planning Time: N.N ms
  Execution Time: N.N ms
 (4 rows)
 
 select explain_filter('explain (analyze, buffers, format text) select * from int8_tbl i8');
-                                        explain_filter                                         
------------------------------------------------------------------------------------------------
- Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N)
+                                         explain_filter                                          
+-------------------------------------------------------------------------------------------------
+ Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
  Planning Time: N.N ms
  Execution Time: N.N ms
 (3 rows)
@@ -102,7 +102,7 @@ select explain_filter('explain (analyze, buffers, format xml) select * from int8
        <Plan-Width>N</Plan-Width>                      +
        <Actual-Startup-Time>N.N</Actual-Startup-Time>  +
        <Actual-Total-Time>N.N</Actual-Total-Time>      +
-       <Actual-Rows>N</Actual-Rows>                    +
+       <Actual-Rows>N.N</Actual-Rows>                  +
        <Actual-Loops>N</Actual-Loops>                  +
        <Disabled>false</Disabled>                      +
        <Shared-Hit-Blocks>N</Shared-Hit-Blocks>        +
@@ -151,7 +151,7 @@ select explain_filter('explain (analyze, serialize, buffers, format yaml) select
      Plan Width: N            +
      Actual Startup Time: N.N +
      Actual Total Time: N.N   +
-     Actual Rows: N           +
+     Actual Rows: N.N         +
      Actual Loops: N          +
      Disabled: false          +
      Shared Hit Blocks: N     +
@@ -263,7 +263,7 @@ select explain_filter('explain (analyze, buffers, format json) select * from int
        "Plan Width": N,             +
        "Actual Startup Time": N.N,  +
        "Actual Total Time": N.N,    +
-       "Actual Rows": N,            +
+       "Actual Rows": N.N,          +
        "Actual Loops": N,           +
        "Disabled": false,           +
        "Shared Hit Blocks": N,      +
@@ -353,9 +353,9 @@ select explain_filter('explain (memory) select * from int8_tbl i8');
 (2 rows)
 
 select explain_filter('explain (memory, analyze, buffers off) select * from int8_tbl i8');
-                                        explain_filter                                         
------------------------------------------------------------------------------------------------
- Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N)
+                                         explain_filter                                          
+-------------------------------------------------------------------------------------------------
+ Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
    Memory: used=NkB  allocated=NkB
  Planning Time: N.N ms
  Execution Time: N.N ms
@@ -398,7 +398,7 @@ select explain_filter('explain (memory, analyze, format json) select * from int8
        "Plan Width": N,            +
        "Actual Startup Time": N.N, +
        "Actual Total Time": N.N,   +
-       "Actual Rows": N,           +
+       "Actual Rows": N.N,         +
        "Actual Loops": N,          +
        "Disabled": false,          +
        "Shared Hit Blocks": N,     +
@@ -493,9 +493,6 @@ select jsonb_pretty(
   -- Also remove its sort-type fields, as those aren't 100% stable
   #- '{0,Plan,Plans,0,Sort Method}'
   #- '{0,Plan,Plans,0,Sort Space Type}'
-  -- Actual Rows can be 0 or 0.0 depending on whether loops>1
-  #- '{0,Plan,Plans,0,Actual Rows}'
-  #- '{0,Plan,Plans,0,Plans,0,Actual Rows}'
 );
                         jsonb_pretty                         
 -------------------------------------------------------------
@@ -531,6 +528,7 @@ select jsonb_pretty(
                              "Plan Rows": 0,                +
                              "Plan Width": 0,               +
                              "Total Cost": 0.0,             +
+                             "Actual Rows": 0.0,            +
                              "Actual Loops": 0,             +
                              "Startup Cost": 0.0,           +
                              "Async Capable": false,        +
@@ -577,6 +575,7 @@ select jsonb_pretty(
                      "Plan Rows": 0,                        +
                      "Plan Width": 0,                       +
                      "Total Cost": 0.0,                     +
+                     "Actual Rows": 0.0,                    +
                      "Actual Loops": 0,                     +
                      "Startup Cost": 0.0,                   +
                      "Async Capable": false,                +
@@ -620,7 +619,7 @@ select jsonb_pretty(
              "Plan Rows": 0,                                +
              "Plan Width": 0,                               +
              "Total Cost": 0.0,                             +
-             "Actual Rows": 0,                              +
+             "Actual Rows": 0.0,                            +
              "Actual Loops": 0,                             +
              "Startup Cost": 0.0,                           +
              "Async Capable": false,                        +
@@ -702,27 +701,27 @@ select explain_filter('explain (verbose) create table test_ctas as select 1');
 
 -- Test SERIALIZE option
 select explain_filter('explain (analyze,buffers off,serialize) select * from int8_tbl i8');
-                                        explain_filter                                         
------------------------------------------------------------------------------------------------
- Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N)
+                                         explain_filter                                          
+-------------------------------------------------------------------------------------------------
+ Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
  Planning Time: N.N ms
  Serialization: time=N.N ms  output=NkB  format=text
  Execution Time: N.N ms
 (4 rows)
 
 select explain_filter('explain (analyze,serialize text,buffers,timing off) select * from int8_tbl i8');
-                                 explain_filter                                  
----------------------------------------------------------------------------------
- Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual rows=N loops=N)
+                                  explain_filter                                   
+-----------------------------------------------------------------------------------
+ Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual rows=N.N loops=N)
  Planning Time: N.N ms
  Serialization: output=NkB  format=text
  Execution Time: N.N ms
 (4 rows)
 
 select explain_filter('explain (analyze,serialize binary,buffers,timing) select * from int8_tbl i8');
-                                        explain_filter                                         
------------------------------------------------------------------------------------------------
- Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N)
+                                         explain_filter                                          
+-------------------------------------------------------------------------------------------------
+ Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
  Planning Time: N.N ms
  Serialization: time=N.N ms  output=NkB  format=binary
  Execution Time: N.N ms
@@ -730,9 +729,9 @@ select explain_filter('explain (analyze,serialize binary,buffers,timing) select
 
 -- this tests an edge case where we have no data to return
 select explain_filter('explain (analyze,buffers off,serialize) create temp table explain_temp as select * from int8_tbl i8');
-                                        explain_filter                                         
------------------------------------------------------------------------------------------------
- Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N loops=N)
+                                         explain_filter                                          
+-------------------------------------------------------------------------------------------------
+ Seq Scan on int8_tbl i8  (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
  Planning Time: N.N ms
  Serialization: time=N.N ms  output=NkB  format=text
  Execution Time: N.N ms
@@ -740,11 +739,11 @@ select explain_filter('explain (analyze,buffers off,serialize) create temp table
 
 -- Test tuplestore storage usage in Window aggregate (memory case)
 select explain_filter('explain (analyze,buffers off,costs off) select sum(n) over() from generate_series(1,10) a(n)');
-                                 explain_filter                                 
---------------------------------------------------------------------------------
- WindowAgg (actual time=N.N..N.N rows=N loops=N)
+                                  explain_filter                                  
+----------------------------------------------------------------------------------
+ WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
    Storage: Memory  Maximum Storage: NkB
-   ->  Function Scan on generate_series a (actual time=N.N..N.N rows=N loops=N)
+   ->  Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
  Planning Time: N.N ms
  Execution Time: N.N ms
 (5 rows)
@@ -752,25 +751,25 @@ select explain_filter('explain (analyze,buffers off,costs off) select sum(n) ove
 -- Test tuplestore storage usage in Window aggregate (disk case)
 set work_mem to 64;
 select explain_filter('explain (analyze,buffers off,costs off) select sum(n) over() from generate_series(1,2000) a(n)');
-                                 explain_filter                                 
---------------------------------------------------------------------------------
- WindowAgg (actual time=N.N..N.N rows=N loops=N)
+                                  explain_filter                                  
+----------------------------------------------------------------------------------
+ WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
    Storage: Disk  Maximum Storage: NkB
-   ->  Function Scan on generate_series a (actual time=N.N..N.N rows=N loops=N)
+   ->  Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
  Planning Time: N.N ms
  Execution Time: N.N ms
 (5 rows)
 
 -- Test tuplestore storage usage in Window aggregate (memory and disk case, final result is disk)
 select explain_filter('explain (analyze,buffers off,costs off) select sum(n) over(partition by m) from (SELECT n < 3 as m, n from generate_series(1,2000) a(n))');
-                                    explain_filter                                    
---------------------------------------------------------------------------------------
- WindowAgg (actual time=N.N..N.N rows=N loops=N)
+                                     explain_filter                                     
+----------------------------------------------------------------------------------------
+ WindowAgg (actual time=N.N..N.N rows=N.N loops=N)
    Storage: Disk  Maximum Storage: NkB
-   ->  Sort (actual time=N.N..N.N rows=N loops=N)
+   ->  Sort (actual time=N.N..N.N rows=N.N loops=N)
          Sort Key: ((a.n < N))
          Sort Method: external merge  Disk: NkB
-         ->  Function Scan on generate_series a (actual time=N.N..N.N rows=N loops=N)
+         ->  Function Scan on generate_series a (actual time=N.N..N.N rows=N.N loops=N)
  Planning Time: N.N ms
  Execution Time: N.N ms
 (8 rows)
diff --git a/src/test/regress/expected/gin.out b/src/test/regress/expected/gin.out
index 0af464309ee..118bc26d8cd 100644
--- a/src/test/regress/expected/gin.out
+++ b/src/test/regress/expected/gin.out
@@ -187,16 +187,16 @@ from
   lateral execute_text_query_heap($$select string_agg((i, j)::text, ' ') from t_gin_test_tbl where $$ || query) res_heap;
                    query                   | return by index | removed by recheck | match 
 -------------------------------------------+-----------------+--------------------+-------
-  i @> '{}'                                | 7               | 0                  | t
-  j @> '{}'                                | 6               | 0                  | t
-  i @> '{}' and j @> '{}'                  | 4               | 0                  | t
-  i @> '{1}'                               | 5               | 0                  | t
-  i @> '{1}' and j @> '{}'                 | 3               | 0                  | t
-  i @> '{1}' and i @> '{}' and j @> '{}'   | 3               | 0                  | t
-  j @> '{10}'                              | 4               | 0                  | t
-  j @> '{10}' and i @> '{}'                | 3               | 0                  | t
-  j @> '{10}' and j @> '{}' and i @> '{}'  | 3               | 0                  | t
-  i @> '{1}' and j @> '{10}'               | 2               | 0                  | t
+  i @> '{}'                                | 7.00            | 0                  | t
+  j @> '{}'                                | 6.00            | 0                  | t
+  i @> '{}' and j @> '{}'                  | 4.00            | 0                  | t
+  i @> '{1}'                               | 5.00            | 0                  | t
+  i @> '{1}' and j @> '{}'                 | 3.00            | 0                  | t
+  i @> '{1}' and i @> '{}' and j @> '{}'   | 3.00            | 0                  | t
+  j @> '{10}'                              | 4.00            | 0                  | t
+  j @> '{10}' and i @> '{}'                | 3.00            | 0                  | t
+  j @> '{10}' and j @> '{}' and i @> '{}'  | 3.00            | 0                  | t
+  i @> '{1}' and j @> '{10}'               | 2.00            | 0                  | t
 (10 rows)
 
 reset enable_seqscan;
diff --git a/src/test/regress/expected/incremental_sort.out b/src/test/regress/expected/incremental_sort.out
index d5975758409..b00219643b9 100644
--- a/src/test/regress/expected/incremental_sort.out
+++ b/src/test/regress/expected/incremental_sort.out
@@ -522,15 +522,15 @@ select * from (select * from t order by a) s order by a, b limit 55;
 select explain_analyze_without_memory('select * from (select * from t order by a) s order by a, b limit 55');
                                         explain_analyze_without_memory                                         
 ---------------------------------------------------------------------------------------------------------------
- Limit (actual rows=55 loops=1)
-   ->  Incremental Sort (actual rows=55 loops=1)
+ Limit (actual rows=55.00 loops=1)
+   ->  Incremental Sort (actual rows=55.00 loops=1)
          Sort Key: t.a, t.b
          Presorted Key: t.a
          Full-sort Groups: 2  Sort Methods: top-N heapsort, quicksort  Average Memory: NNkB  Peak Memory: NNkB
-         ->  Sort (actual rows=101 loops=1)
+         ->  Sort (actual rows=101.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: NNkB
-               ->  Seq Scan on t (actual rows=1000 loops=1)
+               ->  Seq Scan on t (actual rows=1000.00 loops=1)
 (9 rows)
 
 select jsonb_pretty(explain_analyze_inc_sort_nodes_without_memory('select * from (select * from t order by a) s order by a, b limit 55'));
@@ -544,7 +544,7 @@ select jsonb_pretty(explain_analyze_inc_sort_nodes_without_memory('select * from
              "t.b"                              +
          ],                                     +
          "Node Type": "Incremental Sort",       +
-         "Actual Rows": 55,                     +
+         "Actual Rows": 55.00,                  +
          "Actual Loops": 1,                     +
          "Async Capable": false,                +
          "Presorted Key": [                     +
@@ -726,16 +726,16 @@ rollback;
 select explain_analyze_without_memory('select * from (select * from t order by a) s order by a, b limit 70');
                                          explain_analyze_without_memory                                         
 ----------------------------------------------------------------------------------------------------------------
- Limit (actual rows=70 loops=1)
-   ->  Incremental Sort (actual rows=70 loops=1)
+ Limit (actual rows=70.00 loops=1)
+   ->  Incremental Sort (actual rows=70.00 loops=1)
          Sort Key: t.a, t.b
          Presorted Key: t.a
          Full-sort Groups: 1  Sort Method: quicksort  Average Memory: NNkB  Peak Memory: NNkB
          Pre-sorted Groups: 5  Sort Methods: top-N heapsort, quicksort  Average Memory: NNkB  Peak Memory: NNkB
-         ->  Sort (actual rows=1000 loops=1)
+         ->  Sort (actual rows=1000.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: NNkB
-               ->  Seq Scan on t (actual rows=1000 loops=1)
+               ->  Seq Scan on t (actual rows=1000.00 loops=1)
 (10 rows)
 
 select jsonb_pretty(explain_analyze_inc_sort_nodes_without_memory('select * from (select * from t order by a) s order by a, b limit 70'));
@@ -749,7 +749,7 @@ select jsonb_pretty(explain_analyze_inc_sort_nodes_without_memory('select * from
              "t.b"                              +
          ],                                     +
          "Node Type": "Incremental Sort",       +
-         "Actual Rows": 70,                     +
+         "Actual Rows": 70.00,                  +
          "Actual Loops": 1,                     +
          "Async Capable": false,                +
          "Presorted Key": [                     +
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 9eab51bc2a6..54939ecc6b0 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -627,10 +627,10 @@ CREATE MATERIALIZED VIEW matview_schema.mv_withdata1 (a) AS
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
   CREATE MATERIALIZED VIEW matview_schema.mv_withdata2 (a) AS
   SELECT generate_series(1, 10) WITH DATA;
-              QUERY PLAN              
---------------------------------------
- ProjectSet (actual rows=10 loops=1)
-   ->  Result (actual rows=1 loops=1)
+               QUERY PLAN                
+-----------------------------------------
+ ProjectSet (actual rows=10.00 loops=1)
+   ->  Result (actual rows=1.00 loops=1)
 (2 rows)
 
 REFRESH MATERIALIZED VIEW matview_schema.mv_withdata2;
diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out
index dbd01066d00..22f2d32845e 100644
--- a/src/test/regress/expected/memoize.out
+++ b/src/test/regress/expected/memoize.out
@@ -37,9 +37,9 @@ INNER JOIN tenk1 t2 ON t1.unique1 = t2.twenty
 WHERE t2.unique1 < 1000;', false);
                                        explain_memoize                                        
 ----------------------------------------------------------------------------------------------
- Aggregate (actual rows=1 loops=N)
-   ->  Nested Loop (actual rows=1000 loops=N)
-         ->  Seq Scan on tenk1 t2 (actual rows=1000 loops=N)
+ Aggregate (actual rows=1.00 loops=N)
+   ->  Nested Loop (actual rows=1000.00 loops=N)
+         ->  Seq Scan on tenk1 t2 (actual rows=1000.00 loops=N)
                Filter: (unique1 < 1000)
                Rows Removed by Filter: 9000
          ->  Memoize (actual rows=1.00 loops=N)
@@ -68,9 +68,9 @@ LATERAL (SELECT t2.unique1 FROM tenk1 t2
 WHERE t1.unique1 < 1000;', false);
                                        explain_memoize                                        
 ----------------------------------------------------------------------------------------------
- Aggregate (actual rows=1 loops=N)
-   ->  Nested Loop (actual rows=1000 loops=N)
-         ->  Seq Scan on tenk1 t1 (actual rows=1000 loops=N)
+ Aggregate (actual rows=1.00 loops=N)
+   ->  Nested Loop (actual rows=1000.00 loops=N)
+         ->  Seq Scan on tenk1 t1 (actual rows=1000.00 loops=N)
                Filter: (unique1 < 1000)
                Rows Removed by Filter: 9000
          ->  Memoize (actual rows=1.00 loops=N)
@@ -102,9 +102,9 @@ ON t1.two = t2.two
 WHERE t1.unique1 < 10;', false);
                                          explain_memoize                                         
 -------------------------------------------------------------------------------------------------
- Aggregate (actual rows=1 loops=N)
-   ->  Nested Loop Left Join (actual rows=20 loops=N)
-         ->  Index Scan using tenk1_unique1 on tenk1 t1 (actual rows=10 loops=N)
+ Aggregate (actual rows=1.00 loops=N)
+   ->  Nested Loop Left Join (actual rows=20.00 loops=N)
+         ->  Index Scan using tenk1_unique1 on tenk1 t1 (actual rows=10.00 loops=N)
                Index Cond: (unique1 < 10)
          ->  Memoize (actual rows=2.00 loops=N)
                Cache Key: t1.two
@@ -136,9 +136,9 @@ LATERAL (SELECT t1.two+1 AS c1, t2.unique1 AS c2 FROM tenk1 t2) s ON TRUE
 WHERE s.c1 = s.c2 AND t1.unique1 < 1000;', false);
                                        explain_memoize                                        
 ----------------------------------------------------------------------------------------------
- Aggregate (actual rows=1 loops=N)
-   ->  Nested Loop (actual rows=1000 loops=N)
-         ->  Seq Scan on tenk1 t1 (actual rows=1000 loops=N)
+ Aggregate (actual rows=1.00 loops=N)
+   ->  Nested Loop (actual rows=1000.00 loops=N)
+         ->  Seq Scan on tenk1 t1 (actual rows=1000.00 loops=N)
                Filter: (unique1 < 1000)
                Rows Removed by Filter: 9000
          ->  Memoize (actual rows=1.00 loops=N)
@@ -168,9 +168,9 @@ ON t1.two = s.two
 WHERE s.c1 = s.c2 AND t1.unique1 < 1000;', false);
                                     explain_memoize                                    
 ---------------------------------------------------------------------------------------
- Aggregate (actual rows=1 loops=N)
-   ->  Nested Loop (actual rows=1000 loops=N)
-         ->  Seq Scan on tenk1 t1 (actual rows=1000 loops=N)
+ Aggregate (actual rows=1.00 loops=N)
+   ->  Nested Loop (actual rows=1000.00 loops=N)
+         ->  Seq Scan on tenk1 t1 (actual rows=1000.00 loops=N)
                Filter: (unique1 < 1000)
                Rows Removed by Filter: 9000
          ->  Memoize (actual rows=1.00 loops=N)
@@ -209,8 +209,8 @@ SELECT * FROM expr_key t1 INNER JOIN expr_key t2
 ON t1.x = t2.t::numeric AND t1.t::numeric = t2.x;', false);
                                        explain_memoize                                        
 ----------------------------------------------------------------------------------------------
- Nested Loop (actual rows=80 loops=N)
-   ->  Seq Scan on expr_key t1 (actual rows=40 loops=N)
+ Nested Loop (actual rows=80.00 loops=N)
+   ->  Seq Scan on expr_key t1 (actual rows=40.00 loops=N)
    ->  Memoize (actual rows=2.00 loops=N)
          Cache Key: t1.x, (t1.t)::numeric
          Cache Mode: logical
@@ -234,9 +234,9 @@ INNER JOIN tenk1 t2 ON t1.unique1 = t2.thousand
 WHERE t2.unique1 < 1200;', true);
                                        explain_memoize                                        
 ----------------------------------------------------------------------------------------------
- Aggregate (actual rows=1 loops=N)
-   ->  Nested Loop (actual rows=1200 loops=N)
-         ->  Seq Scan on tenk1 t2 (actual rows=1200 loops=N)
+ Aggregate (actual rows=1.00 loops=N)
+   ->  Nested Loop (actual rows=1200.00 loops=N)
+         ->  Seq Scan on tenk1 t2 (actual rows=1200.00 loops=N)
                Filter: (unique1 < 1200)
                Rows Removed by Filter: 8800
          ->  Memoize (actual rows=1.00 loops=N)
@@ -256,16 +256,16 @@ SET enable_seqscan TO off;
 -- Ensure memoize operates in logical mode
 SELECT explain_memoize('
 SELECT * FROM flt f1 INNER JOIN flt f2 ON f1.f = f2.f;', false);
-                                explain_memoize                                
--------------------------------------------------------------------------------
- Nested Loop (actual rows=4 loops=N)
-   ->  Index Only Scan using flt_f_idx on flt f1 (actual rows=2 loops=N)
+                                 explain_memoize                                  
+----------------------------------------------------------------------------------
+ Nested Loop (actual rows=4.00 loops=N)
+   ->  Index Only Scan using flt_f_idx on flt f1 (actual rows=2.00 loops=N)
          Heap Fetches: N
    ->  Memoize (actual rows=2.00 loops=N)
          Cache Key: f1.f
          Cache Mode: logical
          Hits: 1  Misses: 1  Evictions: Zero  Overflows: 0  Memory Usage: NkB
-         ->  Index Only Scan using flt_f_idx on flt f2 (actual rows=2 loops=N)
+         ->  Index Only Scan using flt_f_idx on flt f2 (actual rows=2.00 loops=N)
                Index Cond: (f = f1.f)
                Heap Fetches: N
 (10 rows)
@@ -275,8 +275,8 @@ SELECT explain_memoize('
 SELECT * FROM flt f1 INNER JOIN flt f2 ON f1.f >= f2.f;', false);
                                  explain_memoize                                  
 ----------------------------------------------------------------------------------
- Nested Loop (actual rows=4 loops=N)
-   ->  Index Only Scan using flt_f_idx on flt f1 (actual rows=2 loops=N)
+ Nested Loop (actual rows=4.00 loops=N)
+   ->  Index Only Scan using flt_f_idx on flt f1 (actual rows=2.00 loops=N)
          Heap Fetches: N
    ->  Memoize (actual rows=2.00 loops=N)
          Cache Key: f1.f
@@ -302,8 +302,8 @@ SELECT explain_memoize('
 SELECT * FROM strtest s1 INNER JOIN strtest s2 ON s1.n >= s2.n;', false);
                                    explain_memoize                                   
 -------------------------------------------------------------------------------------
- Nested Loop (actual rows=24 loops=N)
-   ->  Seq Scan on strtest s1 (actual rows=6 loops=N)
+ Nested Loop (actual rows=24.00 loops=N)
+   ->  Seq Scan on strtest s1 (actual rows=6.00 loops=N)
          Disabled: true
    ->  Memoize (actual rows=4.00 loops=N)
          Cache Key: s1.n
@@ -318,8 +318,8 @@ SELECT explain_memoize('
 SELECT * FROM strtest s1 INNER JOIN strtest s2 ON s1.t >= s2.t;', false);
                                    explain_memoize                                   
 -------------------------------------------------------------------------------------
- Nested Loop (actual rows=24 loops=N)
-   ->  Seq Scan on strtest s1 (actual rows=6 loops=N)
+ Nested Loop (actual rows=24.00 loops=N)
+   ->  Seq Scan on strtest s1 (actual rows=6.00 loops=N)
          Disabled: true
    ->  Memoize (actual rows=4.00 loops=N)
          Cache Key: s1.t
@@ -342,27 +342,27 @@ CREATE INDEX iprt_p2_a ON prt_p2 (a);
 ANALYZE prt;
 SELECT explain_memoize('
 SELECT * FROM prt t1 INNER JOIN prt t2 ON t1.a = t2.a;', false);
-                                     explain_memoize                                      
-------------------------------------------------------------------------------------------
- Append (actual rows=32 loops=N)
-   ->  Nested Loop (actual rows=16 loops=N)
-         ->  Index Only Scan using iprt_p1_a on prt_p1 t1_1 (actual rows=4 loops=N)
+                                       explain_memoize                                       
+---------------------------------------------------------------------------------------------
+ Append (actual rows=32.00 loops=N)
+   ->  Nested Loop (actual rows=16.00 loops=N)
+         ->  Index Only Scan using iprt_p1_a on prt_p1 t1_1 (actual rows=4.00 loops=N)
                Heap Fetches: N
          ->  Memoize (actual rows=4.00 loops=N)
                Cache Key: t1_1.a
                Cache Mode: logical
                Hits: 3  Misses: 1  Evictions: Zero  Overflows: 0  Memory Usage: NkB
-               ->  Index Only Scan using iprt_p1_a on prt_p1 t2_1 (actual rows=4 loops=N)
+               ->  Index Only Scan using iprt_p1_a on prt_p1 t2_1 (actual rows=4.00 loops=N)
                      Index Cond: (a = t1_1.a)
                      Heap Fetches: N
-   ->  Nested Loop (actual rows=16 loops=N)
-         ->  Index Only Scan using iprt_p2_a on prt_p2 t1_2 (actual rows=4 loops=N)
+   ->  Nested Loop (actual rows=16.00 loops=N)
+         ->  Index Only Scan using iprt_p2_a on prt_p2 t1_2 (actual rows=4.00 loops=N)
                Heap Fetches: N
          ->  Memoize (actual rows=4.00 loops=N)
                Cache Key: t1_2.a
                Cache Mode: logical
                Hits: 3  Misses: 1  Evictions: Zero  Overflows: 0  Memory Usage: NkB
-               ->  Index Only Scan using iprt_p2_a on prt_p2 t2_2 (actual rows=4 loops=N)
+               ->  Index Only Scan using iprt_p2_a on prt_p2 t2_2 (actual rows=4.00 loops=N)
                      Index Cond: (a = t1_2.a)
                      Heap Fetches: N
 (21 rows)
@@ -373,20 +373,20 @@ SELECT explain_memoize('
 SELECT * FROM prt_p1 t1 INNER JOIN
 (SELECT * FROM prt_p1 UNION ALL SELECT * FROM prt_p2) t2
 ON t1.a = t2.a;', false);
-                                   explain_memoize                                   
--------------------------------------------------------------------------------------
- Nested Loop (actual rows=16 loops=N)
-   ->  Index Only Scan using iprt_p1_a on prt_p1 t1 (actual rows=4 loops=N)
+                                    explain_memoize                                     
+----------------------------------------------------------------------------------------
+ Nested Loop (actual rows=16.00 loops=N)
+   ->  Index Only Scan using iprt_p1_a on prt_p1 t1 (actual rows=4.00 loops=N)
          Heap Fetches: N
    ->  Memoize (actual rows=4.00 loops=N)
          Cache Key: t1.a
          Cache Mode: logical
          Hits: 3  Misses: 1  Evictions: Zero  Overflows: 0  Memory Usage: NkB
-         ->  Append (actual rows=4 loops=N)
-               ->  Index Only Scan using iprt_p1_a on prt_p1 (actual rows=4 loops=N)
+         ->  Append (actual rows=4.00 loops=N)
+               ->  Index Only Scan using iprt_p1_a on prt_p1 (actual rows=4.00 loops=N)
                      Index Cond: (a = t1.a)
                      Heap Fetches: N
-               ->  Index Only Scan using iprt_p2_a on prt_p2 (actual rows=0 loops=N)
+               ->  Index Only Scan using iprt_p2_a on prt_p2 (actual rows=0.00 loops=N)
                      Index Cond: (a = t1.a)
                      Heap Fetches: N
 (14 rows)
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 05314ad4397..c8ecc8b6b4a 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -1636,20 +1636,20 @@ SELECT explain_merge('
 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
 WHEN MATCHED THEN
 	UPDATE SET b = t.b + 1');
-                            explain_merge                             
-----------------------------------------------------------------------
- Merge on ex_mtarget t (actual rows=0 loops=1)
+                              explain_merge                              
+-------------------------------------------------------------------------
+ Merge on ex_mtarget t (actual rows=0.00 loops=1)
    Tuples: updated=50
-   ->  Merge Join (actual rows=50 loops=1)
+   ->  Merge Join (actual rows=50.00 loops=1)
          Merge Cond: (t.a = s.a)
-         ->  Sort (actual rows=50 loops=1)
+         ->  Sort (actual rows=50.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_mtarget t (actual rows=50 loops=1)
-         ->  Sort (actual rows=100 loops=1)
+               ->  Seq Scan on ex_mtarget t (actual rows=50.00 loops=1)
+         ->  Sort (actual rows=100.00 loops=1)
                Sort Key: s.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_msource s (actual rows=100 loops=1)
+               ->  Seq Scan on ex_msource s (actual rows=100.00 loops=1)
 (12 rows)
 
 -- only updates to selected tuples
@@ -1657,20 +1657,20 @@ SELECT explain_merge('
 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
 WHEN MATCHED AND t.a < 10 THEN
 	UPDATE SET b = t.b + 1');
-                            explain_merge                             
-----------------------------------------------------------------------
- Merge on ex_mtarget t (actual rows=0 loops=1)
+                              explain_merge                              
+-------------------------------------------------------------------------
+ Merge on ex_mtarget t (actual rows=0.00 loops=1)
    Tuples: updated=5 skipped=45
-   ->  Merge Join (actual rows=50 loops=1)
+   ->  Merge Join (actual rows=50.00 loops=1)
          Merge Cond: (t.a = s.a)
-         ->  Sort (actual rows=50 loops=1)
+         ->  Sort (actual rows=50.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_mtarget t (actual rows=50 loops=1)
-         ->  Sort (actual rows=100 loops=1)
+               ->  Seq Scan on ex_mtarget t (actual rows=50.00 loops=1)
+         ->  Sort (actual rows=100.00 loops=1)
                Sort Key: s.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_msource s (actual rows=100 loops=1)
+               ->  Seq Scan on ex_msource s (actual rows=100.00 loops=1)
 (12 rows)
 
 -- updates + deletes
@@ -1680,20 +1680,20 @@ WHEN MATCHED AND t.a < 10 THEN
 	UPDATE SET b = t.b + 1
 WHEN MATCHED AND t.a >= 10 AND t.a <= 20 THEN
 	DELETE');
-                            explain_merge                             
-----------------------------------------------------------------------
- Merge on ex_mtarget t (actual rows=0 loops=1)
+                              explain_merge                              
+-------------------------------------------------------------------------
+ Merge on ex_mtarget t (actual rows=0.00 loops=1)
    Tuples: updated=5 deleted=5 skipped=40
-   ->  Merge Join (actual rows=50 loops=1)
+   ->  Merge Join (actual rows=50.00 loops=1)
          Merge Cond: (t.a = s.a)
-         ->  Sort (actual rows=50 loops=1)
+         ->  Sort (actual rows=50.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_mtarget t (actual rows=50 loops=1)
-         ->  Sort (actual rows=100 loops=1)
+               ->  Seq Scan on ex_mtarget t (actual rows=50.00 loops=1)
+         ->  Sort (actual rows=100.00 loops=1)
                Sort Key: s.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_msource s (actual rows=100 loops=1)
+               ->  Seq Scan on ex_msource s (actual rows=100.00 loops=1)
 (12 rows)
 
 -- only inserts
@@ -1701,20 +1701,20 @@ SELECT explain_merge('
 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
 WHEN NOT MATCHED AND s.a < 10 THEN
 	INSERT VALUES (a, b)');
-                            explain_merge                             
-----------------------------------------------------------------------
- Merge on ex_mtarget t (actual rows=0 loops=1)
+                              explain_merge                              
+-------------------------------------------------------------------------
+ Merge on ex_mtarget t (actual rows=0.00 loops=1)
    Tuples: inserted=4 skipped=96
-   ->  Merge Left Join (actual rows=100 loops=1)
+   ->  Merge Left Join (actual rows=100.00 loops=1)
          Merge Cond: (s.a = t.a)
-         ->  Sort (actual rows=100 loops=1)
+         ->  Sort (actual rows=100.00 loops=1)
                Sort Key: s.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_msource s (actual rows=100 loops=1)
-         ->  Sort (actual rows=45 loops=1)
+               ->  Seq Scan on ex_msource s (actual rows=100.00 loops=1)
+         ->  Sort (actual rows=45.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_mtarget t (actual rows=45 loops=1)
+               ->  Seq Scan on ex_mtarget t (actual rows=45.00 loops=1)
 (12 rows)
 
 -- all three
@@ -1726,20 +1726,20 @@ WHEN MATCHED AND t.a >= 30 AND t.a <= 40 THEN
 	DELETE
 WHEN NOT MATCHED AND s.a < 20 THEN
 	INSERT VALUES (a, b)');
-                            explain_merge                             
-----------------------------------------------------------------------
- Merge on ex_mtarget t (actual rows=0 loops=1)
+                              explain_merge                              
+-------------------------------------------------------------------------
+ Merge on ex_mtarget t (actual rows=0.00 loops=1)
    Tuples: inserted=10 updated=9 deleted=5 skipped=76
-   ->  Merge Left Join (actual rows=100 loops=1)
+   ->  Merge Left Join (actual rows=100.00 loops=1)
          Merge Cond: (s.a = t.a)
-         ->  Sort (actual rows=100 loops=1)
+         ->  Sort (actual rows=100.00 loops=1)
                Sort Key: s.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_msource s (actual rows=100 loops=1)
-         ->  Sort (actual rows=49 loops=1)
+               ->  Seq Scan on ex_msource s (actual rows=100.00 loops=1)
+         ->  Sort (actual rows=49.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_mtarget t (actual rows=49 loops=1)
+               ->  Seq Scan on ex_mtarget t (actual rows=49.00 loops=1)
 (12 rows)
 
 -- not matched by source
@@ -1747,20 +1747,20 @@ SELECT explain_merge('
 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
 WHEN NOT MATCHED BY SOURCE and t.a < 10 THEN
 	DELETE');
-                            explain_merge                             
-----------------------------------------------------------------------
- Merge on ex_mtarget t (actual rows=0 loops=1)
+                              explain_merge                              
+-------------------------------------------------------------------------
+ Merge on ex_mtarget t (actual rows=0.00 loops=1)
    Tuples: skipped=54
-   ->  Merge Left Join (actual rows=54 loops=1)
+   ->  Merge Left Join (actual rows=54.00 loops=1)
          Merge Cond: (t.a = s.a)
-         ->  Sort (actual rows=54 loops=1)
+         ->  Sort (actual rows=54.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_mtarget t (actual rows=54 loops=1)
-         ->  Sort (actual rows=100 loops=1)
+               ->  Seq Scan on ex_mtarget t (actual rows=54.00 loops=1)
+         ->  Sort (actual rows=100.00 loops=1)
                Sort Key: s.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_msource s (actual rows=100 loops=1)
+               ->  Seq Scan on ex_msource s (actual rows=100.00 loops=1)
 (12 rows)
 
 -- not matched by source and target
@@ -1770,20 +1770,20 @@ WHEN NOT MATCHED BY SOURCE AND t.a < 10 THEN
 	DELETE
 WHEN NOT MATCHED BY TARGET AND s.a < 20 THEN
 	INSERT VALUES (a, b)');
-                            explain_merge                             
-----------------------------------------------------------------------
- Merge on ex_mtarget t (actual rows=0 loops=1)
+                              explain_merge                              
+-------------------------------------------------------------------------
+ Merge on ex_mtarget t (actual rows=0.00 loops=1)
    Tuples: skipped=100
-   ->  Merge Full Join (actual rows=100 loops=1)
+   ->  Merge Full Join (actual rows=100.00 loops=1)
          Merge Cond: (t.a = s.a)
-         ->  Sort (actual rows=54 loops=1)
+         ->  Sort (actual rows=54.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_mtarget t (actual rows=54 loops=1)
-         ->  Sort (actual rows=100 loops=1)
+               ->  Seq Scan on ex_mtarget t (actual rows=54.00 loops=1)
+         ->  Sort (actual rows=100.00 loops=1)
                Sort Key: s.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_msource s (actual rows=100 loops=1)
+               ->  Seq Scan on ex_msource s (actual rows=100.00 loops=1)
 (12 rows)
 
 -- nothing
@@ -1791,15 +1791,15 @@ SELECT explain_merge('
 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a AND t.a < -1000
 WHEN MATCHED AND t.a < 10 THEN
 	DO NOTHING');
-                           explain_merge                            
---------------------------------------------------------------------
- Merge on ex_mtarget t (actual rows=0 loops=1)
-   ->  Merge Join (actual rows=0 loops=1)
+                             explain_merge                             
+-----------------------------------------------------------------------
+ Merge on ex_mtarget t (actual rows=0.00 loops=1)
+   ->  Merge Join (actual rows=0.00 loops=1)
          Merge Cond: (t.a = s.a)
-         ->  Sort (actual rows=0 loops=1)
+         ->  Sort (actual rows=0.00 loops=1)
                Sort Key: t.a
                Sort Method: quicksort  Memory: xxx
-               ->  Seq Scan on ex_mtarget t (actual rows=0 loops=1)
+               ->  Seq Scan on ex_mtarget t (actual rows=0.00 loops=1)
                      Filter: (a < '-1000'::integer)
                      Rows Removed by Filter: 54
          ->  Sort (never executed)
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
index 106dedb519a..ad30ad0f26f 100644
--- a/src/test/regress/expected/misc_functions.out
+++ b/src/test/regress/expected/misc_functions.out
@@ -647,27 +647,27 @@ SELECT * FROM tenk1 a JOIN my_gen_series(1,10) g ON a.unique1 = g;
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '1 day') g(s);$$,
 true, true, false, true);
-                                    explain_mask_costs                                    
-------------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=30 width=N) (actual rows=30 loops=1)
+                                     explain_mask_costs                                      
+---------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=30 width=N) (actual rows=30.00 loops=1)
 (1 row)
 
 -- As above but with generate_series_timestamp
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(TIMESTAMP '2024-02-01', TIMESTAMP '2024-03-01', INTERVAL '1 day') g(s);$$,
 true, true, false, true);
-                                    explain_mask_costs                                    
-------------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=30 width=N) (actual rows=30 loops=1)
+                                     explain_mask_costs                                      
+---------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=30 width=N) (actual rows=30.00 loops=1)
 (1 row)
 
 -- As above but with generate_series_timestamptz_at_zone()
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '1 day', 'UTC') g(s);$$,
 true, true, false, true);
-                                    explain_mask_costs                                    
-------------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=30 width=N) (actual rows=30 loops=1)
+                                     explain_mask_costs                                      
+---------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=30 width=N) (actual rows=30.00 loops=1)
 (1 row)
 
 -- Ensure the estimated and actual row counts match when the range isn't
@@ -675,27 +675,27 @@ true, true, false, true);
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '7 day') g(s);$$,
 true, true, false, true);
-                                   explain_mask_costs                                   
-----------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=5 width=N) (actual rows=5 loops=1)
+                                    explain_mask_costs                                     
+-------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=5 width=N) (actual rows=5.00 loops=1)
 (1 row)
 
 -- Ensure the estimates match when step is decreasing
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(TIMESTAMPTZ '2024-03-01', TIMESTAMPTZ '2024-02-01', INTERVAL '-1 day') g(s);$$,
 true, true, false, true);
-                                    explain_mask_costs                                    
-------------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=30 width=N) (actual rows=30 loops=1)
+                                     explain_mask_costs                                      
+---------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=30 width=N) (actual rows=30.00 loops=1)
 (1 row)
 
 -- Ensure an empty range estimates 1 row
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(TIMESTAMPTZ '2024-03-01', TIMESTAMPTZ '2024-02-01', INTERVAL '1 day') g(s);$$,
 true, true, false, true);
-                                   explain_mask_costs                                   
-----------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=1 width=N) (actual rows=0 loops=1)
+                                    explain_mask_costs                                     
+-------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=1 width=N) (actual rows=0.00 loops=1)
 (1 row)
 
 -- Ensure we get the default row estimate for infinity values
@@ -719,36 +719,36 @@ ERROR:  step size cannot equal zero
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(1.0, 25.0) g(s);$$,
 true, true, false, true);
-                                    explain_mask_costs                                    
-------------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=25 width=N) (actual rows=25 loops=1)
+                                     explain_mask_costs                                      
+---------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=25 width=N) (actual rows=25.00 loops=1)
 (1 row)
 
 -- As above but with non-default step
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(1.0, 25.0, 2.0) g(s);$$,
 true, true, false, true);
-                                    explain_mask_costs                                    
-------------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=13 width=N) (actual rows=13 loops=1)
+                                     explain_mask_costs                                      
+---------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=13 width=N) (actual rows=13.00 loops=1)
 (1 row)
 
 -- Ensure the estimates match when step is decreasing
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(25.0, 1.0, -1.0) g(s);$$,
 true, true, false, true);
-                                    explain_mask_costs                                    
-------------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=25 width=N) (actual rows=25 loops=1)
+                                     explain_mask_costs                                      
+---------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=25 width=N) (actual rows=25.00 loops=1)
 (1 row)
 
 -- Ensure an empty range estimates 1 row
 SELECT explain_mask_costs($$
 SELECT * FROM generate_series(25.0, 1.0, 1.0) g(s);$$,
 true, true, false, true);
-                                   explain_mask_costs                                   
-----------------------------------------------------------------------------------------
- Function Scan on generate_series g  (cost=N..N rows=1 width=N) (actual rows=0 loops=1)
+                                    explain_mask_costs                                     
+-------------------------------------------------------------------------------------------
+ Function Scan on generate_series g  (cost=N..N rows=1 width=N) (actual rows=0.00 loops=1)
 (1 row)
 
 -- Ensure we get the default row estimate for error cases (infinity/NaN values
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 1dbe6ff54fb..3d8cfebae61 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2157,34 +2157,34 @@ set enable_indexonlyscan = off;
 prepare ab_q1 (int, int, int) as
 select * from ab where a between $1 and $2 and b <= $3;
 explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q1 (2, 2, 3);
-                       QUERY PLAN                        
----------------------------------------------------------
- Append (actual rows=0 loops=1)
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 6
-   ->  Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b1 ab_1 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
-   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
-   ->  Seq Scan on ab_a2_b3 ab_3 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b3 ab_3 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
 (8 rows)
 
 explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q1 (1, 2, 3);
-                       QUERY PLAN                        
----------------------------------------------------------
- Append (actual rows=0 loops=1)
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 3
-   ->  Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a1_b1 ab_1 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
-   ->  Seq Scan on ab_a1_b2 ab_2 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a1_b2 ab_2 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
-   ->  Seq Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a1_b3 ab_3 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
-   ->  Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b1 ab_4 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
-   ->  Seq Scan on ab_a2_b2 ab_5 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b2 ab_5 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
-   ->  Seq Scan on ab_a2_b3 ab_6 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b3 ab_6 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b <= $3))
 (14 rows)
 
@@ -2193,28 +2193,28 @@ deallocate ab_q1;
 prepare ab_q1 (int, int) as
 select a from ab where a between $1 and $2 and b < 3;
 explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q1 (2, 2);
-                       QUERY PLAN                        
----------------------------------------------------------
- Append (actual rows=0 loops=1)
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 4
-   ->  Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b1 ab_1 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
-   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
 (6 rows)
 
 explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q1 (2, 4);
-                       QUERY PLAN                        
----------------------------------------------------------
- Append (actual rows=0 loops=1)
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 2
-   ->  Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b1 ab_1 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
-   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
-   ->  Seq Scan on ab_a3_b1 ab_3 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a3_b1 ab_3 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
-   ->  Seq Scan on ab_a3_b2 ab_4 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a3_b2 ab_4 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
 (10 rows)
 
@@ -2225,13 +2225,13 @@ select a from ab where a between $1 and $2 and b < (select 3);
 explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q2 (2, 2);
                               QUERY PLAN                               
 -----------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 6
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
-   ->  Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
+   ->  Seq Scan on ab_a2_b1 ab_1 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1))
-   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0.00 loops=1)
          Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1))
    ->  Seq Scan on ab_a2_b3 ab_3 (never executed)
          Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1))
@@ -2243,13 +2243,13 @@ select a from ab where b between $1 and $2 and a < (select 3);
 explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q3 (2, 2);
                               QUERY PLAN                               
 -----------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 6
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
-   ->  Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
+   ->  Seq Scan on ab_a1_b2 ab_1 (actual rows=0.00 loops=1)
          Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1))
-   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b2 ab_2 (actual rows=0.00 loops=1)
          Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1))
    ->  Seq Scan on ab_a3_b2 ab_3 (never executed)
          Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1))
@@ -2303,44 +2303,44 @@ begin;
 create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable;
 -- Ensure pruning works using a stable function containing no Vars
 explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(1);
-                            QUERY PLAN                            
-------------------------------------------------------------------
- Append (actual rows=1 loops=1)
+                             QUERY PLAN                              
+---------------------------------------------------------------------
+ Append (actual rows=1.00 loops=1)
    Subplans Removed: 3
-   ->  Seq Scan on list_part1 list_part_1 (actual rows=1 loops=1)
+   ->  Seq Scan on list_part1 list_part_1 (actual rows=1.00 loops=1)
          Filter: (a = list_part_fn(1))
 (4 rows)
 
 -- Ensure pruning does not take place when the function has a Var parameter
 explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(a);
-                            QUERY PLAN                            
-------------------------------------------------------------------
- Append (actual rows=4 loops=1)
-   ->  Seq Scan on list_part1 list_part_1 (actual rows=1 loops=1)
+                             QUERY PLAN                              
+---------------------------------------------------------------------
+ Append (actual rows=4.00 loops=1)
+   ->  Seq Scan on list_part1 list_part_1 (actual rows=1.00 loops=1)
          Filter: (a = list_part_fn(a))
-   ->  Seq Scan on list_part2 list_part_2 (actual rows=1 loops=1)
+   ->  Seq Scan on list_part2 list_part_2 (actual rows=1.00 loops=1)
          Filter: (a = list_part_fn(a))
-   ->  Seq Scan on list_part3 list_part_3 (actual rows=1 loops=1)
+   ->  Seq Scan on list_part3 list_part_3 (actual rows=1.00 loops=1)
          Filter: (a = list_part_fn(a))
-   ->  Seq Scan on list_part4 list_part_4 (actual rows=1 loops=1)
+   ->  Seq Scan on list_part4 list_part_4 (actual rows=1.00 loops=1)
          Filter: (a = list_part_fn(a))
 (9 rows)
 
 -- Ensure pruning does not take place when the expression contains a Var.
 explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(1) + a;
-                            QUERY PLAN                            
-------------------------------------------------------------------
- Append (actual rows=0 loops=1)
-   ->  Seq Scan on list_part1 list_part_1 (actual rows=0 loops=1)
+                             QUERY PLAN                              
+---------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
+   ->  Seq Scan on list_part1 list_part_1 (actual rows=0.00 loops=1)
          Filter: (a = (list_part_fn(1) + a))
          Rows Removed by Filter: 1
-   ->  Seq Scan on list_part2 list_part_2 (actual rows=0 loops=1)
+   ->  Seq Scan on list_part2 list_part_2 (actual rows=0.00 loops=1)
          Filter: (a = (list_part_fn(1) + a))
          Rows Removed by Filter: 1
-   ->  Seq Scan on list_part3 list_part_3 (actual rows=0 loops=1)
+   ->  Seq Scan on list_part3 list_part_3 (actual rows=0.00 loops=1)
          Filter: (a = (list_part_fn(1) + a))
          Rows Removed by Filter: 1
-   ->  Seq Scan on list_part4 list_part_4 (actual rows=0 loops=1)
+   ->  Seq Scan on list_part4 list_part_4 (actual rows=0.00 loops=1)
          Filter: (a = (list_part_fn(1) + a))
          Rows Removed by Filter: 1
 (13 rows)
@@ -2672,15 +2672,15 @@ reset max_parallel_workers_per_gather;
 -- Test run-time partition pruning with an initplan
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 from lprt_a);
-                               QUERY PLAN                                
--------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                                 QUERY PLAN                                 
+----------------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    InitPlan 1
-     ->  Aggregate (actual rows=1 loops=1)
-           ->  Seq Scan on lprt_a (actual rows=102 loops=1)
+     ->  Aggregate (actual rows=1.00 loops=1)
+           ->  Seq Scan on lprt_a (actual rows=102.00 loops=1)
    InitPlan 2
-     ->  Aggregate (actual rows=1 loops=1)
-           ->  Seq Scan on lprt_a lprt_a_1 (actual rows=102 loops=1)
+     ->  Aggregate (actual rows=1.00 loops=1)
+           ->  Seq Scan on lprt_a lprt_a_1 (actual rows=102.00 loops=1)
    ->  Bitmap Heap Scan on ab_a1_b1 ab_1 (never executed)
          Recheck Cond: (a = (InitPlan 1).col1)
          Filter: (b = (InitPlan 2).col1)
@@ -2716,10 +2716,10 @@ select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1
          Filter: (b = (InitPlan 2).col1)
          ->  Bitmap Index Scan on ab_a3_b1_a_idx (never executed)
                Index Cond: (a = (InitPlan 1).col1)
-   ->  Bitmap Heap Scan on ab_a3_b2 ab_8 (actual rows=0 loops=1)
+   ->  Bitmap Heap Scan on ab_a3_b2 ab_8 (actual rows=0.00 loops=1)
          Recheck Cond: (a = (InitPlan 1).col1)
          Filter: (b = (InitPlan 2).col1)
-         ->  Bitmap Index Scan on ab_a3_b2_a_idx (actual rows=0 loops=1)
+         ->  Bitmap Index Scan on ab_a3_b2_a_idx (actual rows=0.00 loops=1)
                Index Cond: (a = (InitPlan 1).col1)
    ->  Bitmap Heap Scan on ab_a3_b3 ab_9 (never executed)
          Recheck Cond: (a = (InitPlan 1).col1)
@@ -2731,16 +2731,16 @@ select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1
 -- Test run-time partition pruning with UNION ALL parents
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from (select * from ab where a = 1 union all select * from ab) ab where b = (select 1);
-                                  QUERY PLAN                                   
--------------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                                    QUERY PLAN                                    
+----------------------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
-   ->  Append (actual rows=0 loops=1)
-         ->  Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
+   ->  Append (actual rows=0.00 loops=1)
+         ->  Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0.00 loops=1)
                Recheck Cond: (a = 1)
                Filter: (b = (InitPlan 1).col1)
-               ->  Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1)
+               ->  Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0.00 loops=1)
                      Index Cond: (a = 1)
          ->  Bitmap Heap Scan on ab_a1_b2 ab_12 (never executed)
                Recheck Cond: (a = 1)
@@ -2752,19 +2752,19 @@ select * from (select * from ab where a = 1 union all select * from ab) ab where
                Filter: (b = (InitPlan 1).col1)
                ->  Bitmap Index Scan on ab_a1_b3_a_idx (never executed)
                      Index Cond: (a = 1)
-   ->  Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a1_b1 ab_1 (actual rows=0.00 loops=1)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a1_b2 ab_2 (never executed)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a1_b3 ab_3 (never executed)
          Filter: (b = (InitPlan 1).col1)
-   ->  Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b1 ab_4 (actual rows=0.00 loops=1)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a2_b2 ab_5 (never executed)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a2_b3 ab_6 (never executed)
          Filter: (b = (InitPlan 1).col1)
-   ->  Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a3_b1 ab_7 (actual rows=0.00 loops=1)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a3_b2 ab_8 (never executed)
          Filter: (b = (InitPlan 1).col1)
@@ -2775,16 +2775,16 @@ select * from (select * from ab where a = 1 union all select * from ab) ab where
 -- A case containing a UNION ALL with a non-partitioned child.
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from (select * from ab where a = 1 union all (values(10,5)) union all select * from ab) ab where b = (select 1);
-                                  QUERY PLAN                                   
--------------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                                    QUERY PLAN                                    
+----------------------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
-   ->  Append (actual rows=0 loops=1)
-         ->  Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
+   ->  Append (actual rows=0.00 loops=1)
+         ->  Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0.00 loops=1)
                Recheck Cond: (a = 1)
                Filter: (b = (InitPlan 1).col1)
-               ->  Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1)
+               ->  Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0.00 loops=1)
                      Index Cond: (a = 1)
          ->  Bitmap Heap Scan on ab_a1_b2 ab_12 (never executed)
                Recheck Cond: (a = 1)
@@ -2796,21 +2796,21 @@ select * from (select * from ab where a = 1 union all (values(10,5)) union all s
                Filter: (b = (InitPlan 1).col1)
                ->  Bitmap Index Scan on ab_a1_b3_a_idx (never executed)
                      Index Cond: (a = 1)
-   ->  Result (actual rows=0 loops=1)
+   ->  Result (actual rows=0.00 loops=1)
          One-Time Filter: (5 = (InitPlan 1).col1)
-   ->  Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a1_b1 ab_1 (actual rows=0.00 loops=1)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a1_b2 ab_2 (never executed)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a1_b3 ab_3 (never executed)
          Filter: (b = (InitPlan 1).col1)
-   ->  Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a2_b1 ab_4 (actual rows=0.00 loops=1)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a2_b2 ab_5 (never executed)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a2_b3 ab_6 (never executed)
          Filter: (b = (InitPlan 1).col1)
-   ->  Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1)
+   ->  Seq Scan on ab_a3_b1 ab_7 (actual rows=0.00 loops=1)
          Filter: (b = (InitPlan 1).col1)
    ->  Seq Scan on ab_a3_b2 ab_8 (never executed)
          Filter: (b = (InitPlan 1).col1)
@@ -2835,17 +2835,17 @@ union all
 explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q6(1);
                        QUERY PLAN                       
 --------------------------------------------------------
- Append (actual rows=0 loops=1)
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 12
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
    ->  Seq Scan on ab_a1_b1 ab_1 (never executed)
          Filter: ((a = $1) AND (b = (InitPlan 1).col1))
    ->  Seq Scan on ab_a1_b2 ab_2 (never executed)
          Filter: ((a = $1) AND (b = (InitPlan 1).col1))
    ->  Seq Scan on ab_a1_b3 ab_3 (never executed)
          Filter: ((a = $1) AND (b = (InitPlan 1).col1))
-   ->  Seq Scan on xy_1 (actual rows=0 loops=1)
+   ->  Seq Scan on xy_1 (actual rows=0.00 loops=1)
          Filter: ((x = $1) AND (y = (InitPlan 1).col1))
          Rows Removed by Filter: 1
    ->  Seq Scan on ab_a1_b1 ab_4 (never executed)
@@ -2875,44 +2875,44 @@ deallocate ab_q6;
 insert into ab values (1,2);
 select explain_analyze('
 update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a;');
-                                      explain_analyze                                      
--------------------------------------------------------------------------------------------
- Update on ab_a1 (actual rows=0 loops=1)
+                                       explain_analyze                                        
+----------------------------------------------------------------------------------------------
+ Update on ab_a1 (actual rows=0.00 loops=1)
    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=1 loops=1)
-         ->  Append (actual rows=1 loops=1)
-               ->  Bitmap Heap Scan on ab_a1_b1 ab_a1_1 (actual rows=0 loops=1)
+   ->  Nested Loop (actual rows=1.00 loops=1)
+         ->  Append (actual rows=1.00 loops=1)
+               ->  Bitmap Heap Scan on ab_a1_b1 ab_a1_1 (actual rows=0.00 loops=1)
                      Recheck Cond: (a = 1)
-                     ->  Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1)
+                     ->  Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0.00 loops=1)
                            Index Cond: (a = 1)
-               ->  Bitmap Heap Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1)
+               ->  Bitmap Heap Scan on ab_a1_b2 ab_a1_2 (actual rows=1.00 loops=1)
                      Recheck Cond: (a = 1)
                      Heap Blocks: exact=1
-                     ->  Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1)
+                     ->  Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1.00 loops=1)
                            Index Cond: (a = 1)
-               ->  Bitmap Heap Scan on ab_a1_b3 ab_a1_3 (actual rows=0 loops=1)
+               ->  Bitmap Heap Scan on ab_a1_b3 ab_a1_3 (actual rows=0.00 loops=1)
                      Recheck Cond: (a = 1)
                      Heap Blocks: exact=1
-                     ->  Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1)
+                     ->  Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1.00 loops=1)
                            Index Cond: (a = 1)
-         ->  Materialize (actual rows=1 loops=1)
+         ->  Materialize (actual rows=1.00 loops=1)
                Storage: Memory  Maximum Storage: NkB
-               ->  Append (actual rows=1 loops=1)
-                     ->  Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
+               ->  Append (actual rows=1.00 loops=1)
+                     ->  Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0.00 loops=1)
                            Recheck Cond: (a = 1)
-                           ->  Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1)
+                           ->  Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0.00 loops=1)
                                  Index Cond: (a = 1)
-                     ->  Bitmap Heap Scan on ab_a1_b2 ab_2 (actual rows=1 loops=1)
+                     ->  Bitmap Heap Scan on ab_a1_b2 ab_2 (actual rows=1.00 loops=1)
                            Recheck Cond: (a = 1)
                            Heap Blocks: exact=1
-                           ->  Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1)
+                           ->  Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1.00 loops=1)
                                  Index Cond: (a = 1)
-                     ->  Bitmap Heap Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1)
+                     ->  Bitmap Heap Scan on ab_a1_b3 ab_3 (actual rows=0.00 loops=1)
                            Recheck Cond: (a = 1)
                            Heap Blocks: exact=1
-                           ->  Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1)
+                           ->  Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1.00 loops=1)
                                  Index Cond: (a = 1)
 (37 rows)
 
@@ -2927,23 +2927,23 @@ truncate ab;
 insert into ab values (1, 1), (1, 2), (1, 3), (2, 1);
 select explain_analyze('
 update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1);');
-                               explain_analyze                                
-------------------------------------------------------------------------------
- Update on ab_a1 (actual rows=0 loops=1)
+                                 explain_analyze                                 
+---------------------------------------------------------------------------------
+ Update on ab_a1 (actual rows=0.00 loops=1)
    Update on ab_a1_b1 ab_a1_1
    Update on ab_a1_b2 ab_a1_2
    Update on ab_a1_b3 ab_a1_3
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
-   ->  Nested Loop (actual rows=3 loops=1)
-         ->  Append (actual rows=3 loops=1)
-               ->  Seq Scan on ab_a1_b1 ab_a1_1 (actual rows=1 loops=1)
-               ->  Seq Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1)
-               ->  Seq Scan on ab_a1_b3 ab_a1_3 (actual rows=1 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
+   ->  Nested Loop (actual rows=3.00 loops=1)
+         ->  Append (actual rows=3.00 loops=1)
+               ->  Seq Scan on ab_a1_b1 ab_a1_1 (actual rows=1.00 loops=1)
+               ->  Seq Scan on ab_a1_b2 ab_a1_2 (actual rows=1.00 loops=1)
+               ->  Seq Scan on ab_a1_b3 ab_a1_3 (actual rows=1.00 loops=1)
          ->  Materialize (actual rows=1.00 loops=3)
                Storage: Memory  Maximum Storage: NkB
-               ->  Append (actual rows=1 loops=1)
-                     ->  Seq Scan on ab_a2_b1 ab_a2_1 (actual rows=1 loops=1)
+               ->  Append (actual rows=1.00 loops=1)
+                     ->  Seq Scan on ab_a2_b1 ab_a2_1 (actual rows=1.00 loops=1)
                            Filter: (b = (InitPlan 1).col1)
                      ->  Seq Scan on ab_a2_b2 ab_a2_2 (never executed)
                            Filter: (b = (InitPlan 1).col1)
@@ -2985,12 +2985,12 @@ explain (analyze, costs off, summary off, timing off, buffers off)
 select * from tbl1 join tprt on tbl1.col1 > tprt.col1;
                                  QUERY PLAN                                  
 -----------------------------------------------------------------------------
- Nested Loop (actual rows=6 loops=1)
-   ->  Seq Scan on tbl1 (actual rows=2 loops=1)
+ Nested Loop (actual rows=6.00 loops=1)
+   ->  Seq Scan on tbl1 (actual rows=2.00 loops=1)
    ->  Append (actual rows=3.00 loops=2)
          ->  Index Scan using tprt1_idx on tprt_1 (actual rows=2.00 loops=2)
                Index Cond: (col1 < tbl1.col1)
-         ->  Index Scan using tprt2_idx on tprt_2 (actual rows=2 loops=1)
+         ->  Index Scan using tprt2_idx on tprt_2 (actual rows=2.00 loops=1)
                Index Cond: (col1 < tbl1.col1)
          ->  Index Scan using tprt3_idx on tprt_3 (never executed)
                Index Cond: (col1 < tbl1.col1)
@@ -3006,8 +3006,8 @@ explain (analyze, costs off, summary off, timing off, buffers off)
 select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
                                  QUERY PLAN                                  
 -----------------------------------------------------------------------------
- Nested Loop (actual rows=2 loops=1)
-   ->  Seq Scan on tbl1 (actual rows=2 loops=1)
+ Nested Loop (actual rows=2.00 loops=1)
+   ->  Seq Scan on tbl1 (actual rows=2.00 loops=1)
    ->  Append (actual rows=1.00 loops=2)
          ->  Index Scan using tprt1_idx on tprt_1 (never executed)
                Index Cond: (col1 = tbl1.col1)
@@ -3051,8 +3051,8 @@ explain (analyze, costs off, summary off, timing off, buffers off)
 select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1;
                                  QUERY PLAN                                  
 -----------------------------------------------------------------------------
- Nested Loop (actual rows=23 loops=1)
-   ->  Seq Scan on tbl1 (actual rows=5 loops=1)
+ Nested Loop (actual rows=23.00 loops=1)
+   ->  Seq Scan on tbl1 (actual rows=5.00 loops=1)
    ->  Append (actual rows=4.60 loops=5)
          ->  Index Scan using tprt1_idx on tprt_1 (actual rows=2.00 loops=5)
                Index Cond: (col1 < tbl1.col1)
@@ -3072,8 +3072,8 @@ explain (analyze, costs off, summary off, timing off, buffers off)
 select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1;
                                  QUERY PLAN                                  
 -----------------------------------------------------------------------------
- Nested Loop (actual rows=3 loops=1)
-   ->  Seq Scan on tbl1 (actual rows=5 loops=1)
+ Nested Loop (actual rows=3.00 loops=1)
+   ->  Seq Scan on tbl1 (actual rows=5.00 loops=1)
    ->  Append (actual rows=0.60 loops=5)
          ->  Index Scan using tprt1_idx on tprt_1 (never executed)
                Index Cond: (col1 = tbl1.col1)
@@ -3134,11 +3134,11 @@ delete from tbl1;
 insert into tbl1 values (4400);
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from tbl1 join tprt on tbl1.col1 < tprt.col1;
-                                QUERY PLAN                                
---------------------------------------------------------------------------
- Nested Loop (actual rows=1 loops=1)
-   ->  Seq Scan on tbl1 (actual rows=1 loops=1)
-   ->  Append (actual rows=1 loops=1)
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
+ Nested Loop (actual rows=1.00 loops=1)
+   ->  Seq Scan on tbl1 (actual rows=1.00 loops=1)
+   ->  Append (actual rows=1.00 loops=1)
          ->  Index Scan using tprt1_idx on tprt_1 (never executed)
                Index Cond: (col1 > tbl1.col1)
          ->  Index Scan using tprt2_idx on tprt_2 (never executed)
@@ -3149,7 +3149,7 @@ select * from tbl1 join tprt on tbl1.col1 < tprt.col1;
                Index Cond: (col1 > tbl1.col1)
          ->  Index Scan using tprt5_idx on tprt_5 (never executed)
                Index Cond: (col1 > tbl1.col1)
-         ->  Index Scan using tprt6_idx on tprt_6 (actual rows=1 loops=1)
+         ->  Index Scan using tprt6_idx on tprt_6 (actual rows=1.00 loops=1)
                Index Cond: (col1 > tbl1.col1)
 (15 rows)
 
@@ -3168,9 +3168,9 @@ explain (analyze, costs off, summary off, timing off, buffers off)
 select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
                             QUERY PLAN                             
 -------------------------------------------------------------------
- Nested Loop (actual rows=0 loops=1)
-   ->  Seq Scan on tbl1 (actual rows=1 loops=1)
-   ->  Append (actual rows=0 loops=1)
+ Nested Loop (actual rows=0.00 loops=1)
+   ->  Seq Scan on tbl1 (actual rows=1.00 loops=1)
+   ->  Append (actual rows=0.00 loops=1)
          ->  Index Scan using tprt1_idx on tprt_1 (never executed)
                Index Cond: (col1 = tbl1.col1)
          ->  Index Scan using tprt2_idx on tprt_2 (never executed)
@@ -3205,9 +3205,9 @@ prepare part_abc_q1 (int, int, int) as
 select * from part_abc where a = $1 and b = $2 and c = $3;
 -- Single partition should be scanned.
 explain (analyze, costs off, summary off, timing off, buffers off) execute part_abc_q1 (1, 2, 3);
-                        QUERY PLAN                        
-----------------------------------------------------------
- Seq Scan on part_abc_p1 part_abc (actual rows=0 loops=1)
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Seq Scan on part_abc_p1 part_abc (actual rows=0.00 loops=1)
    Filter: ((a = $1) AND (b = $2) AND (c = $3))
 (2 rows)
 
@@ -3230,28 +3230,28 @@ select * from listp where b = 1;
 -- which match the given parameter.
 prepare q1 (int,int) as select * from listp where b in ($1,$2);
 explain (analyze, costs off, summary off, timing off, buffers off)  execute q1 (1,1);
-                         QUERY PLAN                          
--------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 1
-   ->  Seq Scan on listp_1_1 listp_1 (actual rows=0 loops=1)
+   ->  Seq Scan on listp_1_1 listp_1 (actual rows=0.00 loops=1)
          Filter: (b = ANY (ARRAY[$1, $2]))
 (4 rows)
 
 explain (analyze, costs off, summary off, timing off, buffers off)  execute q1 (2,2);
-                         QUERY PLAN                          
--------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 1
-   ->  Seq Scan on listp_2_1 listp_1 (actual rows=0 loops=1)
+   ->  Seq Scan on listp_2_1 listp_1 (actual rows=0.00 loops=1)
          Filter: (b = ANY (ARRAY[$1, $2]))
 (4 rows)
 
 -- Try with no matching partitions.
 explain (analyze, costs off, summary off, timing off, buffers off)  execute q1 (0,0);
-           QUERY PLAN           
---------------------------------
- Append (actual rows=0 loops=1)
+            QUERY PLAN             
+-----------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 2
 (2 rows)
 
@@ -3262,17 +3262,17 @@ prepare q1 (int,int,int,int) as select * from listp where b in($1,$2) and $3 <>
 explain (analyze, costs off, summary off, timing off, buffers off)  execute q1 (1,2,2,0);
                                QUERY PLAN                                
 -------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 1
-   ->  Seq Scan on listp_1_1 listp_1 (actual rows=0 loops=1)
+   ->  Seq Scan on listp_1_1 listp_1 (actual rows=0.00 loops=1)
          Filter: ((b = ANY (ARRAY[$1, $2])) AND ($3 <> b) AND ($4 <> b))
 (4 rows)
 
 -- Both partitions allowed by IN clause, then both excluded again by <> clauses.
 explain (analyze, costs off, summary off, timing off, buffers off)  execute q1 (1,2,2,1);
-           QUERY PLAN           
---------------------------------
- Append (actual rows=0 loops=1)
+            QUERY PLAN             
+-----------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 2
 (2 rows)
 
@@ -3281,9 +3281,9 @@ explain (analyze, costs off, summary off, timing off, buffers off)
 select * from listp where a = (select null::int);
                       QUERY PLAN                      
 ------------------------------------------------------
- Append (actual rows=0 loops=1)
+ Append (actual rows=0.00 loops=1)
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
    ->  Seq Scan on listp_1_1 listp_1 (never executed)
          Filter: (a = (InitPlan 1).col1)
    ->  Seq Scan on listp_2_1 listp_2 (never executed)
@@ -3304,24 +3304,24 @@ create table stable_qual_pruning3 partition of stable_qual_pruning
 -- comparison against a stable value requires run-time pruning
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from stable_qual_pruning where a < localtimestamp;
-                                      QUERY PLAN                                      
---------------------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                                       QUERY PLAN                                        
+-----------------------------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 1
-   ->  Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1)
+   ->  Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0.00 loops=1)
          Filter: (a < LOCALTIMESTAMP)
-   ->  Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0 loops=1)
+   ->  Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0.00 loops=1)
          Filter: (a < LOCALTIMESTAMP)
 (6 rows)
 
 -- timestamp < timestamptz comparison is only stable, not immutable
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from stable_qual_pruning where a < '2000-02-01'::timestamptz;
-                                      QUERY PLAN                                      
---------------------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                                       QUERY PLAN                                        
+-----------------------------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 2
-   ->  Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1)
+   ->  Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0.00 loops=1)
          Filter: (a < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)
 (4 rows)
 
@@ -3329,9 +3329,9 @@ select * from stable_qual_pruning where a < '2000-02-01'::timestamptz;
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from stable_qual_pruning
   where a = any(array['2010-02-01', '2020-01-01']::timestamp[]);
-           QUERY PLAN           
---------------------------------
- Result (actual rows=0 loops=1)
+            QUERY PLAN             
+-----------------------------------
+ Result (actual rows=0.00 loops=1)
    One-Time Filter: false
 (2 rows)
 
@@ -3340,7 +3340,7 @@ select * from stable_qual_pruning
   where a = any(array['2000-02-01', '2010-01-01']::timestamp[]);
                                                    QUERY PLAN                                                   
 ----------------------------------------------------------------------------------------------------------------
- Seq Scan on stable_qual_pruning2 stable_qual_pruning (actual rows=0 loops=1)
+ Seq Scan on stable_qual_pruning2 stable_qual_pruning (actual rows=0.00 loops=1)
    Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000","Fri Jan 01 00:00:00 2010"}'::timestamp without time zone[]))
 (2 rows)
 
@@ -3349,18 +3349,18 @@ select * from stable_qual_pruning
   where a = any(array['2000-02-01', localtimestamp]::timestamp[]);
                                                  QUERY PLAN                                                 
 ------------------------------------------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 2
-   ->  Seq Scan on stable_qual_pruning2 stable_qual_pruning_1 (actual rows=0 loops=1)
+   ->  Seq Scan on stable_qual_pruning2 stable_qual_pruning_1 (actual rows=0.00 loops=1)
          Filter: (a = ANY (ARRAY['Tue Feb 01 00:00:00 2000'::timestamp without time zone, LOCALTIMESTAMP]))
 (4 rows)
 
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from stable_qual_pruning
   where a = any(array['2010-02-01', '2020-01-01']::timestamptz[]);
-           QUERY PLAN           
---------------------------------
- Append (actual rows=0 loops=1)
+            QUERY PLAN             
+-----------------------------------
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 3
 (2 rows)
 
@@ -3369,23 +3369,23 @@ select * from stable_qual_pruning
   where a = any(array['2000-02-01', '2010-01-01']::timestamptz[]);
                                                         QUERY PLAN                                                         
 ---------------------------------------------------------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+ Append (actual rows=0.00 loops=1)
    Subplans Removed: 2
-   ->  Seq Scan on stable_qual_pruning2 stable_qual_pruning_1 (actual rows=0 loops=1)
+   ->  Seq Scan on stable_qual_pruning2 stable_qual_pruning_1 (actual rows=0.00 loops=1)
          Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000 PST","Fri Jan 01 00:00:00 2010 PST"}'::timestamp with time zone[]))
 (4 rows)
 
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from stable_qual_pruning
   where a = any(null::timestamptz[]);
-                                      QUERY PLAN                                      
---------------------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
-   ->  Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1)
+                                       QUERY PLAN                                        
+-----------------------------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
+   ->  Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0.00 loops=1)
          Filter: (a = ANY (NULL::timestamp with time zone[]))
-   ->  Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0 loops=1)
+   ->  Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0.00 loops=1)
          Filter: (a = ANY (NULL::timestamp with time zone[]))
-   ->  Seq Scan on stable_qual_pruning3 stable_qual_pruning_3 (actual rows=0 loops=1)
+   ->  Seq Scan on stable_qual_pruning3 stable_qual_pruning_3 (actual rows=0.00 loops=1)
          Filter: (a = ANY (NULL::timestamp with time zone[]))
 (7 rows)
 
@@ -3405,14 +3405,14 @@ create table mc3p2 partition of mc3p
 insert into mc3p values (0, 1, 1), (1, 1, 1), (2, 1, 1);
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from mc3p where a < 3 and abs(b) = 1;
-                       QUERY PLAN                       
---------------------------------------------------------
- Append (actual rows=3 loops=1)
-   ->  Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1)
+                        QUERY PLAN                         
+-----------------------------------------------------------
+ Append (actual rows=3.00 loops=1)
+   ->  Seq Scan on mc3p0 mc3p_1 (actual rows=1.00 loops=1)
          Filter: ((a < 3) AND (abs(b) = 1))
-   ->  Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1)
+   ->  Seq Scan on mc3p1 mc3p_2 (actual rows=1.00 loops=1)
          Filter: ((a < 3) AND (abs(b) = 1))
-   ->  Seq Scan on mc3p2 mc3p_3 (actual rows=1 loops=1)
+   ->  Seq Scan on mc3p2 mc3p_3 (actual rows=1.00 loops=1)
          Filter: ((a < 3) AND (abs(b) = 1))
 (7 rows)
 
@@ -3427,11 +3427,11 @@ explain (analyze, costs off, summary off, timing off, buffers off)
 execute ps1(1);
                          QUERY PLAN                          
 -------------------------------------------------------------
- Append (actual rows=1 loops=1)
+ Append (actual rows=1.00 loops=1)
    Subplans Removed: 2
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
-   ->  Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
+   ->  Seq Scan on mc3p1 mc3p_1 (actual rows=1.00 loops=1)
          Filter: ((a = $1) AND (abs(b) < (InitPlan 1).col1))
 (6 rows)
 
@@ -3442,13 +3442,13 @@ explain (analyze, costs off, summary off, timing off, buffers off)
 execute ps2(1);
                           QUERY PLAN                          
 --------------------------------------------------------------
- Append (actual rows=2 loops=1)
+ Append (actual rows=2.00 loops=1)
    Subplans Removed: 1
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
-   ->  Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
+   ->  Seq Scan on mc3p0 mc3p_1 (actual rows=1.00 loops=1)
          Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1))
-   ->  Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1)
+   ->  Seq Scan on mc3p1 mc3p_2 (actual rows=1.00 loops=1)
          Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1))
 (8 rows)
 
@@ -3462,29 +3462,29 @@ create table boolp_t partition of boolp for values in('t');
 create table boolp_f partition of boolp for values in('f');
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from boolp where a = (select value from boolvalues where value);
-                        QUERY PLAN                         
------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    InitPlan 1
-     ->  Seq Scan on boolvalues (actual rows=1 loops=1)
+     ->  Seq Scan on boolvalues (actual rows=1.00 loops=1)
            Filter: value
            Rows Removed by Filter: 1
    ->  Seq Scan on boolp_f boolp_1 (never executed)
          Filter: (a = (InitPlan 1).col1)
-   ->  Seq Scan on boolp_t boolp_2 (actual rows=0 loops=1)
+   ->  Seq Scan on boolp_t boolp_2 (actual rows=0.00 loops=1)
          Filter: (a = (InitPlan 1).col1)
 (9 rows)
 
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from boolp where a = (select value from boolvalues where not value);
-                        QUERY PLAN                         
------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    InitPlan 1
-     ->  Seq Scan on boolvalues (actual rows=1 loops=1)
+     ->  Seq Scan on boolvalues (actual rows=1.00 loops=1)
            Filter: (NOT value)
            Rows Removed by Filter: 1
-   ->  Seq Scan on boolp_f boolp_1 (actual rows=0 loops=1)
+   ->  Seq Scan on boolp_f boolp_1 (actual rows=0.00 loops=1)
          Filter: (a = (InitPlan 1).col1)
    ->  Seq Scan on boolp_t boolp_2 (never executed)
          Filter: (a = (InitPlan 1).col1)
@@ -3505,15 +3505,15 @@ create index on ma_test (b);
 analyze ma_test;
 prepare mt_q1 (int) as select a from ma_test where a >= $1 and a % 10 = 5 order by b;
 explain (analyze, costs off, summary off, timing off, buffers off) execute mt_q1(15);
-                                       QUERY PLAN                                        
------------------------------------------------------------------------------------------
- Merge Append (actual rows=2 loops=1)
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Merge Append (actual rows=2.00 loops=1)
    Sort Key: ma_test.b
    Subplans Removed: 1
-   ->  Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_1 (actual rows=1 loops=1)
+   ->  Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_1 (actual rows=1.00 loops=1)
          Filter: ((a >= $1) AND ((a % 10) = 5))
          Rows Removed by Filter: 9
-   ->  Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_2 (actual rows=1 loops=1)
+   ->  Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_2 (actual rows=1.00 loops=1)
          Filter: ((a >= $1) AND ((a % 10) = 5))
          Rows Removed by Filter: 9
 (9 rows)
@@ -3526,12 +3526,12 @@ execute mt_q1(15);
 (2 rows)
 
 explain (analyze, costs off, summary off, timing off, buffers off) execute mt_q1(25);
-                                       QUERY PLAN                                        
------------------------------------------------------------------------------------------
- Merge Append (actual rows=1 loops=1)
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Merge Append (actual rows=1.00 loops=1)
    Sort Key: ma_test.b
    Subplans Removed: 2
-   ->  Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_1 (actual rows=1 loops=1)
+   ->  Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_1 (actual rows=1.00 loops=1)
          Filter: ((a >= $1) AND ((a % 10) = 5))
          Rows Removed by Filter: 9
 (6 rows)
@@ -3544,9 +3544,9 @@ execute mt_q1(25);
 
 -- Ensure MergeAppend behaves correctly when no subplans match
 explain (analyze, costs off, summary off, timing off, buffers off) execute mt_q1(35);
-              QUERY PLAN              
---------------------------------------
- Merge Append (actual rows=0 loops=1)
+               QUERY PLAN                
+-----------------------------------------
+ Merge Append (actual rows=0.00 loops=1)
    Sort Key: ma_test.b
    Subplans Removed: 3
 (3 rows)
@@ -3560,11 +3560,11 @@ deallocate mt_q1;
 prepare mt_q2 (int) as select * from ma_test where a >= $1 order by b limit 1;
 -- Ensure output list looks sane when the MergeAppend has no subplans.
 explain (analyze, verbose, costs off, summary off, timing off, buffers off) execute mt_q2 (35);
-                 QUERY PLAN                 
---------------------------------------------
- Limit (actual rows=0 loops=1)
+                  QUERY PLAN                   
+-----------------------------------------------
+ Limit (actual rows=0.00 loops=1)
    Output: ma_test.a, ma_test.b
-   ->  Merge Append (actual rows=0 loops=1)
+   ->  Merge Append (actual rows=0.00 loops=1)
          Sort Key: ma_test.b
          Subplans Removed: 3
 (5 rows)
@@ -3572,21 +3572,21 @@ explain (analyze, verbose, costs off, summary off, timing off, buffers off) exec
 deallocate mt_q2;
 -- ensure initplan params properly prune partitions
 explain (analyze, costs off, summary off, timing off, buffers off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b;
-                                          QUERY PLAN                                           
------------------------------------------------------------------------------------------------
- Merge Append (actual rows=20 loops=1)
+                                            QUERY PLAN                                            
+--------------------------------------------------------------------------------------------------
+ Merge Append (actual rows=20.00 loops=1)
    Sort Key: ma_test.b
    InitPlan 2
-     ->  Result (actual rows=1 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
            InitPlan 1
-             ->  Limit (actual rows=1 loops=1)
-                   ->  Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1)
+             ->  Limit (actual rows=1.00 loops=1)
+                   ->  Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1.00 loops=1)
                          Index Cond: (b IS NOT NULL)
    ->  Index Scan using ma_test_p1_b_idx on ma_test_p1 ma_test_1 (never executed)
          Filter: (a >= (InitPlan 2).col1)
-   ->  Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10 loops=1)
+   ->  Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10.00 loops=1)
          Filter: (a >= (InitPlan 2).col1)
-   ->  Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10 loops=1)
+   ->  Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10.00 loops=1)
          Filter: (a >= (InitPlan 2).col1)
 (14 rows)
 
@@ -4023,9 +4023,9 @@ create table listp2 partition of listp for values in(2) partition by list(b);
 create table listp2_10 partition of listp2 for values in (10);
 explain (analyze, costs off, summary off, timing off, buffers off)
 select * from listp where a = (select 2) and b <> 10;
-                    QUERY PLAN                     
----------------------------------------------------
- Seq Scan on listp1 listp (actual rows=0 loops=1)
+                     QUERY PLAN                      
+-----------------------------------------------------
+ Seq Scan on listp1 listp (actual rows=0.00 loops=1)
    Filter: ((b <> 10) AND (a = (InitPlan 1).col1))
    InitPlan 1
      ->  Result (never executed)
@@ -4148,22 +4148,22 @@ create index on rangep (a);
 -- Ensure run-time pruning works on the nested Merge Append
 explain (analyze on, costs off, timing off, summary off, buffers off)
 select * from rangep where b IN((select 1),(select 2)) order by a;
-                                                 QUERY PLAN                                                 
-------------------------------------------------------------------------------------------------------------
- Append (actual rows=0 loops=1)
+                                                  QUERY PLAN                                                   
+---------------------------------------------------------------------------------------------------------------
+ Append (actual rows=0.00 loops=1)
    InitPlan 1
-     ->  Result (actual rows=1 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
    InitPlan 2
-     ->  Result (actual rows=1 loops=1)
-   ->  Merge Append (actual rows=0 loops=1)
+     ->  Result (actual rows=1.00 loops=1)
+   ->  Merge Append (actual rows=0.00 loops=1)
          Sort Key: rangep_2.a
-         ->  Index Scan using rangep_0_to_100_1_a_idx on rangep_0_to_100_1 rangep_2 (actual rows=0 loops=1)
+         ->  Index Scan using rangep_0_to_100_1_a_idx on rangep_0_to_100_1 rangep_2 (actual rows=0.00 loops=1)
                Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1]))
-         ->  Index Scan using rangep_0_to_100_2_a_idx on rangep_0_to_100_2 rangep_3 (actual rows=0 loops=1)
+         ->  Index Scan using rangep_0_to_100_2_a_idx on rangep_0_to_100_2 rangep_3 (actual rows=0.00 loops=1)
                Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1]))
          ->  Index Scan using rangep_0_to_100_3_a_idx on rangep_0_to_100_3 rangep_4 (never executed)
                Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1]))
-   ->  Index Scan using rangep_100_to_200_a_idx on rangep_100_to_200 rangep_5 (actual rows=0 loops=1)
+   ->  Index Scan using rangep_100_to_200_a_idx on rangep_100_to_200 rangep_5 (actual rows=0.00 loops=1)
          Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1]))
 (15 rows)
 
diff --git a/src/test/regress/expected/select.out b/src/test/regress/expected/select.out
index 88911ca2b9f..cd79abc35db 100644
--- a/src/test/regress/expected/select.out
+++ b/src/test/regress/expected/select.out
@@ -759,9 +759,9 @@ select * from onek2 where unique2 = 11 and stringu1 = 'ATAAAA';
 -- actually run the query with an analyze to use the partial index
 explain (costs off, analyze on, timing off, summary off, buffers off)
 select * from onek2 where unique2 = 11 and stringu1 = 'ATAAAA';
-                           QUERY PLAN                            
------------------------------------------------------------------
- Index Scan using onek2_u2_prtl on onek2 (actual rows=1 loops=1)
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Index Scan using onek2_u2_prtl on onek2 (actual rows=1.00 loops=1)
    Index Cond: (unique2 = 11)
    Filter: (stringu1 = 'ATAAAA'::name)
 (3 rows)
diff --git a/src/test/regress/expected/select_into.out b/src/test/regress/expected/select_into.out
index 5a45eac5ff9..d04ca2b1bf7 100644
--- a/src/test/regress/expected/select_into.out
+++ b/src/test/regress/expected/select_into.out
@@ -28,10 +28,10 @@ ERROR:  permission denied for table tbl_withdata1
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
   CREATE TABLE selinto_schema.tbl_withdata2 (a) AS
   SELECT generate_series(1,3) WITH DATA;
-              QUERY PLAN              
---------------------------------------
- ProjectSet (actual rows=3 loops=1)
-   ->  Result (actual rows=1 loops=1)
+               QUERY PLAN                
+-----------------------------------------
+ ProjectSet (actual rows=3.00 loops=1)
+   ->  Result (actual rows=1.00 loops=1)
 (2 rows)
 
 -- WITH NO DATA, passes.
@@ -53,10 +53,10 @@ CREATE TABLE selinto_schema.tbl_withdata3 (a) AS
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
   CREATE TABLE selinto_schema.tbl_withdata4 (a) AS
   EXECUTE data_sel WITH DATA;
-              QUERY PLAN              
---------------------------------------
- ProjectSet (actual rows=3 loops=1)
-   ->  Result (actual rows=1 loops=1)
+               QUERY PLAN                
+-----------------------------------------
+ ProjectSet (actual rows=3.00 loops=1)
+   ->  Result (actual rows=1.00 loops=1)
 (2 rows)
 
 -- EXECUTE and WITH NO DATA, passes.
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index 4732ad6bfb4..56509540f2a 100644
--- a/src/test/regress/expected/select_parallel.out
+++ b/src/test/regress/expected/select_parallel.out
@@ -585,9 +585,9 @@ explain (analyze, timing off, summary off, costs off, buffers off)
         and tenk2.thousand=0;
                                  QUERY PLAN                                  
 -----------------------------------------------------------------------------
- Aggregate (actual rows=1 loops=1)
-   ->  Nested Loop (actual rows=98000 loops=1)
-         ->  Seq Scan on tenk2 (actual rows=10 loops=1)
+ Aggregate (actual rows=1.00 loops=1)
+   ->  Nested Loop (actual rows=98000.00 loops=1)
+         ->  Seq Scan on tenk2 (actual rows=10.00 loops=1)
                Filter: (thousand = 0)
                Rows Removed by Filter: 9990
          ->  Gather (actual rows=9800.00 loops=10)
@@ -619,8 +619,8 @@ $$;
 select * from explain_parallel_sort_stats();
                          explain_parallel_sort_stats                         
 -----------------------------------------------------------------------------
- Nested Loop Left Join (actual rows=30000 loops=1)
-   ->  Values Scan on "*VALUES*" (actual rows=3 loops=1)
+ Nested Loop Left Join (actual rows=30000.00 loops=1)
+   ->  Values Scan on "*VALUES*" (actual rows=3.00 loops=1)
    ->  Gather Merge (actual rows=10000.00 loops=3)
          Workers Planned: 4
          Workers Launched: 4
@@ -1172,7 +1172,7 @@ SET LOCAL debug_parallel_query = 1;
 EXPLAIN (analyze, timing off, summary off, costs off, buffers off) SELECT * FROM tenk1;
                            QUERY PLAN                           
 ----------------------------------------------------------------
- Gather (actual rows=10000 loops=1)
+ Gather (actual rows=10000.00 loops=1)
    Workers Planned: 4
    Workers Launched: 4
    ->  Parallel Seq Scan on tenk1 (actual rows=2000.00 loops=5)
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index ebc545e2461..d0db8a412ff 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -1726,14 +1726,14 @@ begin
 end;
 $$;
 select * from explain_sq_limit();
-                        explain_sq_limit                        
-----------------------------------------------------------------
- Limit (actual rows=3 loops=1)
-   ->  Subquery Scan on x (actual rows=3 loops=1)
-         ->  Sort (actual rows=3 loops=1)
+                         explain_sq_limit                          
+-------------------------------------------------------------------
+ Limit (actual rows=3.00 loops=1)
+   ->  Subquery Scan on x (actual rows=3.00 loops=1)
+         ->  Sort (actual rows=3.00 loops=1)
                Sort Key: sq_limit.c1, sq_limit.pk
                Sort Method: top-N heapsort  Memory: xxx
-               ->  Seq Scan on sq_limit (actual rows=8 loops=1)
+               ->  Seq Scan on sq_limit (actual rows=8.00 loops=1)
 (6 rows)
 
 select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3;
diff --git a/src/test/regress/expected/tidscan.out b/src/test/regress/expected/tidscan.out
index f6ebdf0601f..e823bc91c57 100644
--- a/src/test/regress/expected/tidscan.out
+++ b/src/test/regress/expected/tidscan.out
@@ -191,10 +191,10 @@ FETCH NEXT FROM c;
 -- perform update
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
 UPDATE tidscan SET id = -id WHERE CURRENT OF c RETURNING *;
-                    QUERY PLAN                     
----------------------------------------------------
- Update on tidscan (actual rows=1 loops=1)
-   ->  Tid Scan on tidscan (actual rows=1 loops=1)
+                      QUERY PLAN                      
+------------------------------------------------------
+ Update on tidscan (actual rows=1.00 loops=1)
+   ->  Tid Scan on tidscan (actual rows=1.00 loops=1)
          TID Cond: CURRENT OF c
 (3 rows)
 
@@ -207,10 +207,10 @@ FETCH NEXT FROM c;
 -- perform update
 EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
 UPDATE tidscan SET id = -id WHERE CURRENT OF c RETURNING *;
-                    QUERY PLAN                     
----------------------------------------------------
- Update on tidscan (actual rows=1 loops=1)
-   ->  Tid Scan on tidscan (actual rows=1 loops=1)
+                      QUERY PLAN                      
+------------------------------------------------------
+ Update on tidscan (actual rows=1.00 loops=1)
+   ->  Tid Scan on tidscan (actual rows=1.00 loops=1)
          TID Cond: CURRENT OF c
 (3 rows)
 
diff --git a/src/test/regress/sql/explain.sql b/src/test/regress/sql/explain.sql
index c719cca6583..0bafa870496 100644
--- a/src/test/regress/sql/explain.sql
+++ b/src/test/regress/sql/explain.sql
@@ -147,9 +147,6 @@ select jsonb_pretty(
   -- Also remove its sort-type fields, as those aren't 100% stable
   #- '{0,Plan,Plans,0,Sort Method}'
   #- '{0,Plan,Plans,0,Sort Space Type}'
-  -- Actual Rows can be 0 or 0.0 depending on whether loops>1
-  #- '{0,Plan,Plans,0,Actual Rows}'
-  #- '{0,Plan,Plans,0,Plans,0,Actual Rows}'
 );
 
 rollback;
-- 
2.39.3 (Apple Git-145)

