@@ -1820,19 +1820,58 @@ mod tests {
18201820 name : & ' static str ,
18211821 pruned : usize ,
18221822 matched : usize ,
1823+ ) -> Arc < Metric > {
1824+ pruning_metric_parts ( name, pruned, matched, 0 , None )
1825+ }
1826+
1827+ fn pruning_metric_parts (
1828+ name : & ' static str ,
1829+ pruned : usize ,
1830+ matched : usize ,
1831+ fully_matched : usize ,
1832+ partition : Option < usize > ,
18231833 ) -> Arc < Metric > {
18241834 let pruning_metrics = PruningMetrics :: new ( ) ;
18251835 pruning_metrics. add_pruned ( pruned) ;
18261836 pruning_metrics. add_matched ( matched) ;
1837+ pruning_metrics. add_fully_matched ( fully_matched) ;
18271838 Arc :: new ( Metric :: new (
18281839 MetricValue :: PruningMetrics {
18291840 name : Cow :: Borrowed ( name) ,
18301841 pruning_metrics,
18311842 } ,
1832- None ,
1843+ partition ,
18331844 ) )
18341845 }
18351846
1847+ fn indent_and_graphviz_metric_displays ( plan : & dyn ExecutionPlan ) -> [ String ; 4 ] {
1848+ [
1849+ DisplayableExecutionPlan :: with_metrics ( plan)
1850+ . indent ( false )
1851+ . to_string ( ) ,
1852+ DisplayableExecutionPlan :: with_full_metrics ( plan)
1853+ . indent ( false )
1854+ . to_string ( ) ,
1855+ DisplayableExecutionPlan :: with_metrics ( plan)
1856+ . graphviz ( )
1857+ . to_string ( ) ,
1858+ DisplayableExecutionPlan :: with_full_metrics ( plan)
1859+ . graphviz ( )
1860+ . to_string ( ) ,
1861+ ]
1862+ }
1863+
1864+ fn pgjson_extras ( plan : & dyn ExecutionPlan , full : bool ) -> serde_json:: Value {
1865+ let display = if full {
1866+ DisplayableExecutionPlan :: with_full_metrics ( plan)
1867+ } else {
1868+ DisplayableExecutionPlan :: with_metrics ( plan)
1869+ } ;
1870+ let out = display. pgjson ( false ) . to_string ( ) ;
1871+ let value: serde_json:: Value = serde_json:: from_str ( & out) . unwrap ( ) ;
1872+ value[ 0 ] [ "Plan" ] [ "Extras" ] . clone ( )
1873+ }
1874+
18361875 #[ test]
18371876 fn pgjson_renders_plan_without_metrics ( ) {
18381877 let plan = sample_plan ( ) ;
@@ -2014,6 +2053,128 @@ mod tests {
20142053 }
20152054 }
20162055
2056+ #[ test]
2057+ fn nonzero_bloom_pruned_or_fully_matched_metrics_are_visible ( ) {
2058+ let mut pruned_metrics = MetricsSet :: new ( ) ;
2059+ pruned_metrics. push ( pruning_metric ( BLOOM_FILTER_PRUNING_METRIC_NAME , 3 , 0 ) ) ;
2060+ let pruned_plan = plan_with_metrics ( pruned_metrics) ;
2061+
2062+ for rendered in indent_and_graphviz_metric_displays ( pruned_plan. as_ref ( ) ) {
2063+ assert ! (
2064+ rendered
2065+ . contains( "row_groups_pruned_bloom_filter=3 total → 0 matched" )
2066+ ) ;
2067+ }
2068+ for extras in [
2069+ pgjson_extras ( pruned_plan. as_ref ( ) , false ) ,
2070+ pgjson_extras ( pruned_plan. as_ref ( ) , true ) ,
2071+ ] {
2072+ assert_eq ! (
2073+ extras[ BLOOM_FILTER_PRUNING_METRIC_NAME ] . as_str( ) ,
2074+ Some ( "3 total → 0 matched" )
2075+ ) ;
2076+ }
2077+
2078+ let mut fully_matched_metrics = MetricsSet :: new ( ) ;
2079+ fully_matched_metrics. push ( pruning_metric_parts (
2080+ BLOOM_FILTER_PRUNING_METRIC_NAME ,
2081+ 0 ,
2082+ 0 ,
2083+ 2 ,
2084+ None ,
2085+ ) ) ;
2086+ let fully_matched_plan = plan_with_metrics ( fully_matched_metrics) ;
2087+
2088+ for rendered in
2089+ indent_and_graphviz_metric_displays ( fully_matched_plan. as_ref ( ) )
2090+ {
2091+ assert ! ( rendered. contains(
2092+ "row_groups_pruned_bloom_filter=0 total → 0 matched -> 2 fully matched"
2093+ ) ) ;
2094+ }
2095+ for extras in [
2096+ pgjson_extras ( fully_matched_plan. as_ref ( ) , false ) ,
2097+ pgjson_extras ( fully_matched_plan. as_ref ( ) , true ) ,
2098+ ] {
2099+ assert_eq ! (
2100+ extras[ BLOOM_FILTER_PRUNING_METRIC_NAME ] . as_str( ) ,
2101+ Some ( "0 total → 0 matched -> 2 fully matched" )
2102+ ) ;
2103+ }
2104+ }
2105+
2106+ #[ test]
2107+ fn idle_and_active_bloom_metrics_on_distinct_partitions ( ) {
2108+ let mut metrics = MetricsSet :: new ( ) ;
2109+ metrics. push ( pruning_metric_parts (
2110+ BLOOM_FILTER_PRUNING_METRIC_NAME ,
2111+ 0 ,
2112+ 0 ,
2113+ 0 ,
2114+ Some ( 0 ) ,
2115+ ) ) ;
2116+ metrics. push ( pruning_metric_parts (
2117+ BLOOM_FILTER_PRUNING_METRIC_NAME ,
2118+ 0 ,
2119+ 2 ,
2120+ 0 ,
2121+ Some ( 1 ) ,
2122+ ) ) ;
2123+ let plan = plan_with_metrics ( metrics) ;
2124+
2125+ let aggregated = [
2126+ DisplayableExecutionPlan :: with_metrics ( plan. as_ref ( ) )
2127+ . indent ( false )
2128+ . to_string ( ) ,
2129+ DisplayableExecutionPlan :: with_metrics ( plan. as_ref ( ) )
2130+ . graphviz ( )
2131+ . to_string ( ) ,
2132+ ] ;
2133+ for rendered in & aggregated {
2134+ assert_eq ! (
2135+ rendered. matches( BLOOM_FILTER_PRUNING_METRIC_NAME ) . count( ) ,
2136+ 1
2137+ ) ;
2138+ assert ! (
2139+ rendered
2140+ . contains( "row_groups_pruned_bloom_filter=2 total → 2 matched" )
2141+ ) ;
2142+ assert ! ( !rendered. contains( "partition=0" ) ) ;
2143+ assert ! ( !rendered. contains( "partition=1" ) ) ;
2144+ }
2145+
2146+ let full = [
2147+ DisplayableExecutionPlan :: with_full_metrics ( plan. as_ref ( ) )
2148+ . indent ( false )
2149+ . to_string ( ) ,
2150+ DisplayableExecutionPlan :: with_full_metrics ( plan. as_ref ( ) )
2151+ . graphviz ( )
2152+ . to_string ( ) ,
2153+ ] ;
2154+ for rendered in & full {
2155+ assert_eq ! (
2156+ rendered. matches( BLOOM_FILTER_PRUNING_METRIC_NAME ) . count( ) ,
2157+ 1
2158+ ) ;
2159+ assert ! ( rendered. contains(
2160+ "row_groups_pruned_bloom_filter{partition=1}=2 total → 2 matched"
2161+ ) ) ;
2162+ assert ! ( !rendered. contains( "partition=0" ) ) ;
2163+ }
2164+
2165+ // PostgreSQL JSON extras are keyed by metric name, so partition
2166+ // labels are not retained; omit still keeps the active totals.
2167+ for extras in [
2168+ pgjson_extras ( plan. as_ref ( ) , false ) ,
2169+ pgjson_extras ( plan. as_ref ( ) , true ) ,
2170+ ] {
2171+ assert_eq ! (
2172+ extras[ BLOOM_FILTER_PRUNING_METRIC_NAME ] . as_str( ) ,
2173+ Some ( "2 total → 2 matched" )
2174+ ) ;
2175+ }
2176+ }
2177+
20172178 #[ test]
20182179 fn pgjson_includes_summary_when_set ( ) {
20192180 let plan = sample_plan ( ) ;
0 commit comments