From e31b9ac1dd859db1f95e3063816f289455da0cf1 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@vondra.me>
Date: Mon, 27 Jul 2026 15:50:30 +0200
Subject: [PATCH v8 17/20] Show expected filter selectivity in EXPLAIN

The selectivity is included in EXPLAIN (VERBOSE) or EXPLAIN (ANALYZE)
---
 src/backend/commands/explain.c                |  4 ++++
 src/test/regress/expected/join.out            | 22 +++++++++----------
 src/test/regress/expected/returning.out       |  6 ++---
 src/test/regress/expected/select_parallel.out |  2 +-
 4 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index eb92d0e094b..f42a593397d 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2676,6 +2676,10 @@ show_bloom_filter_info(PlanState *planstate, List *ancestors,
 			appendStringInfo(&buf, "Bloom Filter %d: keys=%s",
 							 bf->producer_id, keys.data);
 
+			if (es->verbose || es->analyze)
+				appendStringInfo(&buf, " expected=%.1f%%",
+								 bf->selectivity * 100.0);
+
 			/* include the counts only during ANALYZE */
 			if (es->analyze && bfs != NULL)
 			{
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index c56df9ae0d1..23c8b7a3c4a 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -6928,7 +6928,7 @@ where exists (select 1 from t t4
                      Hash Cond: (t6.b = t4.b)
                      ->  Seq Scan on pg_temp.t t6
                            Output: t6.a, t6.b
-                           Bloom Filter 2: keys=(t6.b)
+                           Bloom Filter 2: keys=(t6.b) expected=0.5%
                      ->  Hash
                            Output: t4.b, t5.b, t5.a
                            Bloom Filter 2
@@ -6938,7 +6938,7 @@ where exists (select 1 from t t4
                                  Hash Cond: (t5.b = t4.b)
                                  ->  Seq Scan on pg_temp.t t5
                                        Output: t5.a, t5.b
-                                       Bloom Filter 1: keys=(t5.b)
+                                       Bloom Filter 1: keys=(t5.b) expected=0.5%
                                  ->  Hash
                                        Output: t4.b, t4.a
                                        Bloom Filter 1
@@ -9684,14 +9684,14 @@ select * from j1 inner join j2 on j1.id > j2.id;
 -- ensure non-unique rel is not chosen as inner
 explain (verbose, costs off)
 select * from j1 inner join j3 on j1.id = j3.id;
-              QUERY PLAN              
---------------------------------------
+                     QUERY PLAN                      
+-----------------------------------------------------
  Hash Join
    Output: j1.id, j3.id
    Hash Cond: (j1.id = j3.id)
    ->  Seq Scan on public.j1
          Output: j1.id
-         Bloom Filter 1: keys=(j1.id)
+         Bloom Filter 1: keys=(j1.id) expected=66.7%
    ->  Hash
          Output: j3.id
          Bloom Filter 1
@@ -9786,15 +9786,15 @@ select * from j1 natural join j2;
 explain (verbose, costs off)
 select * from j1
 inner join (select distinct id from j3) j3 on j1.id = j3.id;
-                  QUERY PLAN                   
------------------------------------------------
+                     QUERY PLAN                      
+-----------------------------------------------------
  Hash Join
    Output: j1.id, j3.id
    Inner Unique: true
    Hash Cond: (j1.id = j3.id)
    ->  Seq Scan on public.j1
          Output: j1.id
-         Bloom Filter 1: keys=(j1.id)
+         Bloom Filter 1: keys=(j1.id) expected=33.3%
    ->  Hash
          Output: j3.id
          Bloom Filter 1
@@ -9811,15 +9811,15 @@ inner join (select distinct id from j3) j3 on j1.id = j3.id;
 explain (verbose, costs off)
 select * from j1
 inner join (select id from j3 group by id) j3 on j1.id = j3.id;
-                  QUERY PLAN                   
------------------------------------------------
+                     QUERY PLAN                      
+-----------------------------------------------------
  Hash Join
    Output: j1.id, j3.id
    Inner Unique: true
    Hash Cond: (j1.id = j3.id)
    ->  Seq Scan on public.j1
          Output: j1.id
-         Bloom Filter 1: keys=(j1.id)
+         Bloom Filter 1: keys=(j1.id) expected=33.3%
    ->  Hash
          Output: j3.id
          Bloom Filter 1
diff --git a/src/test/regress/expected/returning.out b/src/test/regress/expected/returning.out
index 50cd3be8030..7cfaa114137 100644
--- a/src/test/regress/expected/returning.out
+++ b/src/test/regress/expected/returning.out
@@ -716,7 +716,7 @@ UPDATE joinview SET f3 = f3 + 1 WHERE f3 = 57
          Hash Cond: (joinme_1.f2j = foo_1.f2)
          ->  Seq Scan on pg_temp.joinme joinme_1
                Output: joinme_1.ctid, joinme_1.f2j
-               Bloom Filter 2: keys=(joinme_1.f2j)
+               Bloom Filter 2: keys=(joinme_1.f2j) expected=2.0%
          ->  Hash
                Output: foo_1.f2, foo_1.tableoid, foo_1.ctid, joinme.ctid, joinme.other, joinme.f2j, foo_2.f1, foo_2.f3, foo_2.ctid, foo_2.f2, foo_2.tableoid
                Bloom Filter 2
@@ -725,7 +725,7 @@ UPDATE joinview SET f3 = f3 + 1 WHERE f3 = 57
                      Hash Cond: (joinme.f2j = foo_1.f2)
                      ->  Seq Scan on pg_temp.joinme
                            Output: joinme.ctid, joinme.other, joinme.f2j
-                           Bloom Filter 1: keys=(joinme.f2j)
+                           Bloom Filter 1: keys=(joinme.f2j) expected=2.0%
                      ->  Hash
                            Output: foo_1.f2, foo_1.tableoid, foo_1.ctid, foo_2.f1, foo_2.f3, foo_2.ctid, foo_2.f2, foo_2.tableoid
                            Bloom Filter 1
@@ -795,7 +795,7 @@ UPDATE joinview SET f3 = f3 + 1, f4 = 7 WHERE f3 = 58
          Hash Cond: (joinme.f2j = foo.f2)
          ->  Seq Scan on pg_temp.joinme
                Output: joinme.other, joinme.ctid, joinme.f2j
-               Bloom Filter 1: keys=(joinme.f2j)
+               Bloom Filter 1: keys=(joinme.f2j) expected=0.5%
          ->  Hash
                Output: foo.f3, foo.f1, foo.f2, foo.f4, foo.ctid, foo.tableoid
                Bloom Filter 1
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index 8f800a66c5e..9d6d213d3e1 100644
--- a/src/test/regress/expected/select_parallel.out
+++ b/src/test/regress/expected/select_parallel.out
@@ -1133,7 +1133,7 @@ explain (costs off, verbose)
          Hash Cond: ((a.unique1 = b.unique1) AND (a.two = (row_number() OVER w1)))
          ->  Seq Scan on public.tenk1 a
                Output: a.unique1, a.unique2, a.two, a.four, a.ten, a.twenty, a.hundred, a.thousand, a.twothousand, a.fivethous, a.tenthous, a.odd, a.even, a.stringu1, a.stringu2, a.string4
-               Bloom Filter 1: keys=(a.unique1, a.two)
+               Bloom Filter 1: keys=(a.unique1, a.two) expected=0.5%
          ->  Hash
                Output: b.unique1, (row_number() OVER w1)
                Bloom Filter 1
-- 
2.55.0

