From 0565c32b066fd03cfdc07336455bf9ac7b3aacde Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Thu, 27 Jun 2024 13:21:58 -0400
Subject: [PATCH v3 4/4] Show number of disabled nodes in EXPLAIN ANALYZE
 output.

Now that disable_cost is not included in the cost estimate, there's
no visible sign in EXPLAIN output of which plan nodes are disabled.
Fix that by propagating the number of disabled nodes from Path to
Plan, and then showing it in the EXPLAIN output.
---
 src/backend/commands/explain.c                |  4 ++++
 src/backend/optimizer/plan/createplan.c       |  6 ++++--
 src/include/nodes/plannodes.h                 |  1 +
 src/test/regress/expected/aggregates.out      | 21 ++++++++++++++++---
 .../regress/expected/collate.icu.utf8.out     |  6 ++++--
 .../regress/expected/incremental_sort.out     |  5 ++++-
 src/test/regress/expected/inherit.out         |  4 +++-
 src/test/regress/expected/join.out            |  4 +++-
 src/test/regress/expected/memoize.out         |  8 +++++--
 src/test/regress/expected/select_parallel.out |  6 +++++-
 src/test/regress/expected/union.out           |  3 ++-
 11 files changed, 54 insertions(+), 14 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 94511a5a02..9147cac1a6 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1889,6 +1889,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	if (es->format == EXPLAIN_FORMAT_TEXT)
 		appendStringInfoChar(es->str, '\n');
 
+	if (plan->disabled_nodes != 0)
+		ExplainPropertyInteger("Disabled Nodes", NULL, plan->disabled_nodes,
+							   es);
+
 	/* prepare per-worker general execution details */
 	if (es->workers_state && es->verbose)
 	{
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 20236e8c4d..1904eea873 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -5409,6 +5409,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 static void
 copy_generic_path_info(Plan *dest, Path *src)
 {
+	dest->disabled_nodes = src->disabled_nodes;
 	dest->startup_cost = src->startup_cost;
 	dest->total_cost = src->total_cost;
 	dest->plan_rows = src->rows;
@@ -5424,6 +5425,7 @@ copy_generic_path_info(Plan *dest, Path *src)
 static void
 copy_plan_costsize(Plan *dest, Plan *src)
 {
+	dest->disabled_nodes = src->disabled_nodes;
 	dest->startup_cost = src->startup_cost;
 	dest->total_cost = src->total_cost;
 	dest->plan_rows = src->plan_rows;
@@ -5457,7 +5459,7 @@ label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples)
 
 	cost_sort(&sort_path, root, NIL,
 			  lefttree->total_cost,
-			  0,				/* a Plan contains no count of disabled nodes */
+			  plan->plan.disabled_nodes,
 			  lefttree->plan_rows,
 			  lefttree->plan_width,
 			  0.0,
@@ -6552,7 +6554,7 @@ materialize_finished_plan(Plan *subplan)
 
 	/* Set cost data */
 	cost_material(&matpath,
-				  0,			/* a Plan contains no count of disabled nodes */
+				  subplan->disabled_nodes,
 				  subplan->startup_cost,
 				  subplan->total_cost,
 				  subplan->plan_rows,
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 1aeeaec95e..62cd6a6666 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -125,6 +125,7 @@ typedef struct Plan
 	/*
 	 * estimated execution costs for plan (see costsize.c for more info)
 	 */
+	int			disabled_nodes; /* count of disabled nodes */
 	Cost		startup_cost;	/* cost expended before fetching any tuples */
 	Cost		total_cost;		/* total cost (assuming all tuples fetched) */
 
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 1c1ca7573a..ab1de1bfd8 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -2895,18 +2895,23 @@ GROUP BY c1.w, c1.z;
                      QUERY PLAN                      
 -----------------------------------------------------
  GroupAggregate
+   Disabled Nodes: 2
    Group Key: c1.w, c1.z
    ->  Sort
+         Disabled Nodes: 2
          Sort Key: c1.w, c1.z, c1.x, c1.y
          ->  Merge Join
+               Disabled Nodes: 2
                Merge Cond: (c1.x = c2.x)
                ->  Sort
                      Sort Key: c1.x
                      ->  Seq Scan on group_agg_pk c1
+                           Disabled Nodes: 1
                ->  Sort
                      Sort Key: c2.x
                      ->  Seq Scan on group_agg_pk c2
-(12 rows)
+                           Disabled Nodes: 1
+(17 rows)
 
 SELECT avg(c1.f ORDER BY c1.x, c1.y)
 FROM group_agg_pk c1 JOIN group_agg_pk c2 ON c1.x = c2.x
@@ -2928,19 +2933,24 @@ GROUP BY c1.y,c1.x,c2.x;
                      QUERY PLAN                      
 -----------------------------------------------------
  Group
+   Disabled Nodes: 2
    Group Key: c1.x, c1.y
    ->  Incremental Sort
+         Disabled Nodes: 2
          Sort Key: c1.x, c1.y
          Presorted Key: c1.x
          ->  Merge Join
+               Disabled Nodes: 2
                Merge Cond: (c1.x = c2.x)
                ->  Sort
                      Sort Key: c1.x
                      ->  Seq Scan on group_agg_pk c1
+                           Disabled Nodes: 1
                ->  Sort
                      Sort Key: c2.x
                      ->  Seq Scan on group_agg_pk c2
-(13 rows)
+                           Disabled Nodes: 1
+(18 rows)
 
 EXPLAIN (COSTS OFF)
 SELECT c1.y,c1.x FROM group_agg_pk c1
@@ -2950,19 +2960,24 @@ GROUP BY c1.y,c2.x,c1.x;
                      QUERY PLAN                      
 -----------------------------------------------------
  Group
+   Disabled Nodes: 2
    Group Key: c2.x, c1.y
    ->  Incremental Sort
+         Disabled Nodes: 2
          Sort Key: c2.x, c1.y
          Presorted Key: c2.x
          ->  Merge Join
+               Disabled Nodes: 2
                Merge Cond: (c1.x = c2.x)
                ->  Sort
                      Sort Key: c1.x
                      ->  Seq Scan on group_agg_pk c1
+                           Disabled Nodes: 1
                ->  Sort
                      Sort Key: c2.x
                      ->  Seq Scan on group_agg_pk c2
-(13 rows)
+                           Disabled Nodes: 1
+(18 rows)
 
 RESET enable_nestloop;
 RESET enable_hashjoin;
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 7d59fb4431..31345295c1 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -989,8 +989,9 @@ select * from collate_test1 where b ilike 'abc';
           QUERY PLAN           
 -------------------------------
  Seq Scan on collate_test1
+   Disabled Nodes: 1
    Filter: (b ~~* 'abc'::text)
-(2 rows)
+(3 rows)
 
 select * from collate_test1 where b ilike 'abc';
  a |  b  
@@ -1004,8 +1005,9 @@ select * from collate_test1 where b ilike 'ABC';
           QUERY PLAN           
 -------------------------------
  Seq Scan on collate_test1
+   Disabled Nodes: 1
    Filter: (b ~~* 'ABC'::text)
-(2 rows)
+(3 rows)
 
 select * from collate_test1 where b ilike 'ABC';
  a |  b  
diff --git a/src/test/regress/expected/incremental_sort.out b/src/test/regress/expected/incremental_sort.out
index 5fd54a10b1..79f0d37a87 100644
--- a/src/test/regress/expected/incremental_sort.out
+++ b/src/test/regress/expected/incremental_sort.out
@@ -701,16 +701,19 @@ explain (costs off) select * from t left join (select * from (select * from t or
                    QUERY PLAN                   
 ------------------------------------------------
  Nested Loop Left Join
+   Disabled Nodes: 1
    Join Filter: (t_1.a = t.a)
    ->  Seq Scan on t
          Filter: (a = ANY ('{1,2}'::integer[]))
    ->  Incremental Sort
+         Disabled Nodes: 1
          Sort Key: t_1.a, t_1.b
          Presorted Key: t_1.a
          ->  Sort
+               Disabled Nodes: 1
                Sort Key: t_1.a
                ->  Seq Scan on t t_1
-(10 rows)
+(13 rows)
 
 select * from t left join (select * from (select * from t order by a) v order by a, b) s on s.a = t.a where t.a in (1, 2);
  a | b | a | b 
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index ad73213414..dbb748a2d2 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1614,6 +1614,7 @@ explain (verbose, costs off) select * from matest0 order by 1-id;
                                QUERY PLAN                               
 ------------------------------------------------------------------------
  Merge Append
+   Disabled Nodes: 1
    Sort Key: ((1 - matest0.id))
    ->  Index Scan using matest0i on public.matest0 matest0_1
          Output: matest0_1.id, matest0_1.name, (1 - matest0_1.id)
@@ -1623,10 +1624,11 @@ explain (verbose, costs off) select * from matest0 order by 1-id;
          Output: matest0_3.id, matest0_3.name, ((1 - matest0_3.id))
          Sort Key: ((1 - matest0_3.id))
          ->  Seq Scan on public.matest2 matest0_3
+               Disabled Nodes: 1
                Output: matest0_3.id, matest0_3.name, (1 - matest0_3.id)
    ->  Index Scan using matest3i on public.matest3 matest0_4
          Output: matest0_4.id, matest0_4.name, (1 - matest0_4.id)
-(13 rows)
+(15 rows)
 
 select * from matest0 order by 1-id;
  id |  name  
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 6b16c3a676..8840fb4e3e 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -7945,13 +7945,15 @@ SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS
                        QUERY PLAN                        
 ---------------------------------------------------------
  Nested Loop Anti Join
+   Disabled Nodes: 1
    ->  Seq Scan on skip_fetch t1
+         Disabled Nodes: 1
    ->  Materialize
          ->  Bitmap Heap Scan on skip_fetch t2
                Recheck Cond: (a = 1)
                ->  Bitmap Index Scan on skip_fetch_a_idx
                      Index Cond: (a = 1)
-(7 rows)
+(9 rows)
 
 SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS NULL;
  a 
diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out
index 0fd103c06b..3b1fd3d95d 100644
--- a/src/test/regress/expected/memoize.out
+++ b/src/test/regress/expected/memoize.out
@@ -240,14 +240,16 @@ SELECT * FROM strtest s1 INNER JOIN strtest s2 ON s1.n >= s2.n;', false);
                                  explain_memoize                                  
 ----------------------------------------------------------------------------------
  Nested Loop (actual rows=24 loops=N)
+   Disabled Nodes: 1
    ->  Seq Scan on strtest s1 (actual rows=6 loops=N)
+         Disabled Nodes: 1
    ->  Memoize (actual rows=4 loops=N)
          Cache Key: s1.n
          Cache Mode: binary
          Hits: 3  Misses: 3  Evictions: Zero  Overflows: 0  Memory Usage: NkB
          ->  Index Scan using strtest_n_idx on strtest s2 (actual rows=4 loops=N)
                Index Cond: (n <= s1.n)
-(8 rows)
+(10 rows)
 
 -- Ensure we get 3 hits and 3 misses
 SELECT explain_memoize('
@@ -255,14 +257,16 @@ SELECT * FROM strtest s1 INNER JOIN strtest s2 ON s1.t >= s2.t;', false);
                                  explain_memoize                                  
 ----------------------------------------------------------------------------------
  Nested Loop (actual rows=24 loops=N)
+   Disabled Nodes: 1
    ->  Seq Scan on strtest s1 (actual rows=6 loops=N)
+         Disabled Nodes: 1
    ->  Memoize (actual rows=4 loops=N)
          Cache Key: s1.t
          Cache Mode: binary
          Hits: 3  Misses: 3  Evictions: Zero  Overflows: 0  Memory Usage: NkB
          ->  Index Scan using strtest_t_idx on strtest s2 (actual rows=4 loops=N)
                Index Cond: (t <= s1.t)
-(8 rows)
+(10 rows)
 
 DROP TABLE strtest;
 -- Ensure memoize works with partitionwise join
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index 20c651aadb..08ef0df9a3 100644
--- a/src/test/regress/expected/select_parallel.out
+++ b/src/test/regress/expected/select_parallel.out
@@ -538,10 +538,14 @@ explain (costs off)
                          QUERY PLAN                         
 ------------------------------------------------------------
  Aggregate
+   Disabled Nodes: 1
    ->  Nested Loop
+         Disabled Nodes: 1
          ->  Gather
+               Disabled Nodes: 1
                Workers Planned: 4
                ->  Parallel Seq Scan on tenk2
+                     Disabled Nodes: 1
                      Filter: (thousand = 0)
          ->  Gather
                Workers Planned: 4
@@ -549,7 +553,7 @@ explain (costs off)
                      Recheck Cond: (hundred > 1)
                      ->  Bitmap Index Scan on tenk1_hundred
                            Index Cond: (hundred > 1)
-(12 rows)
+(16 rows)
 
 select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0;
  count 
diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out
index 0fd0e1c38b..0456d48c93 100644
--- a/src/test/regress/expected/union.out
+++ b/src/test/regress/expected/union.out
@@ -822,11 +822,12 @@ explain (costs off) select '123'::xid union select '123'::xid;
         QUERY PLAN         
 ---------------------------
  HashAggregate
+   Disabled Nodes: 1
    Group Key: ('123'::xid)
    ->  Append
          ->  Result
          ->  Result
-(5 rows)
+(6 rows)
 
 reset enable_hashagg;
 --
-- 
2.39.3 (Apple Git-145)

