Re: Optimize MCV stats for sortable types and utilize sorted-order properties - Mailing list pgsql-hackers

From ZizhuanLiu X-MAN
Subject Re: Optimize MCV stats for sortable types and utilize sorted-order properties
Date
Msg-id tencent_456F3ACB71EAFB460AF8C2B61B5AF389B307@qq.com
Whole thread
In response to Re: Optimize MCV stats for sortable types and utilize sorted-order properties  ("ZizhuanLiu X-MAN" <44973863@qq.com>)
Responses Re: Optimize MCV stats for sortable types and utilize sorted-order properties
List pgsql-hackers
>I write
>From: ZizhuanLiu X-MAN <44973863@qq.com>
>Date: Sep 17, 2026 10:01
>To: ZizhuanLiu X-MAN <44973863@qq.com>, pgsql-hackers <pgsql-hackers@lists.postgresql.org>
>Cc: tgl <tgl@sss.pgh.pa.us>, ilya.evdokimov <ilya.evdokimov@tantorlabs.com>
>Subject: Re: Optimize MCV stats for sortable types and utilize sorted-order properties
>......
>There is another fundamental issue:
>    If the new-version compute_scalar_stats() no longer generates STATISTIC_KIND_MCV, but third-party code or
extensionstry to fetch STATISTIC_KIND_MCV.  When only STATISTIC_KIND_MCV_VALUE_SORTED exists in the system,  
>    - should get_attstatsslot() re-sort STATISTIC_KIND_MCV_VALUE_SORTED by numbers[] in ascending order before
returningit to the caller? 
>           (This re-sort cost should be manageable, since we sort on numbers[], not on values[].)
>    - Or should we return STATISTIC_KIND_MCV_VALUE_SORTED directly without any processing?
>
>For good backward-compatibility, I lean toward the former option, though it is not a very elegant design.


Looking at the call graph in LIST-1:
  When a caller requests STATISTIC_KIND_MCV but only STATISTIC_KIND_MCV_VALUE_SORTED is available in the system,
some reordering is required.

Based on how these callers consume the data, we only need to swap the entry with the largest number[i] and its
corresponding values[i] into slot [0], **rather than performing a full sort of the entire array**.

As shown by the logic in LIST-1: existing callers either rely on slot [0] holding the entry with the highest count
 (the original behaviour of STATISTIC_KIND_MCV), or iterate over the number[] / values[] arrays.

Simply swapping the maximum-count entry to index [0] is sufficient to preserve backward compatibility. This keeps
the adjustment minimal: it requires N comparisons over the double-typed number[] array and at most one two-element
swap,
so the overhead is kept as small as possible.

I will go ahead and implement along these lines. Deep insights and further feedback are very welcome.


>=== LIST-1 ===
                                                                                         
>
                                                                                         
>src/backend/commands/analyze.c
                                                                                         
>  static void compute_distinct_stats() —   producer  ; generates MCV statistics for data types that only have the "="
operator.                                                                                
>  static void compute_scalar_stats() —   producer  ; generates MCV statistics for data types that have both "=" and
"<"operators.                                                                              
>
                                                                                         
>src/backend/executor/nodeHash.c
                                                                                         
>  static void ExecHashBuildSkewHash() —   consumer  ; reads the MCV list and iterates through it, accumulating
sslot.numbers[i].                                                                               
>
                                                                                         
>src/backend/statistics/attribute_stats.c
                                                                                         
>  static bool attribute_statistics_update_internal() —   producer  ; generates MCV statistics from the input
parametersusing statatt_build_stavalues() and updates the MCV statistics with statatt_set_slot(). 
>
                                                                                         
>src/backend/statistics/extended_stats_funcs.c
                                                                                         
>  static Datum import_pg_statistic() —   producer  ; generates MCV statistics from JSONB input.
                                                                                        
>
                                                                                         
>src/backend/utils/adt/network_selfuncs.c
                                                                                         
>  static Selectivity networkjoinsel_inner() —   consumer  ; reads the MCV list and either accumulates mcv_numbers[i]
orcompares values for equality using "=".                                                 
>  static Selectivity networkjoinsel_semi() —   consumer  ; reads the MCV list and either accumulates mcv_numbers[i] or
comparesvalues for equality using "=".                                                  
>
                                                                                         
>src/backend/utils/adt/selfuncs.c
                                                                                         
>  double var_eq_const() —   consumer  ; reads the MCV list, compares values using "=", obtains the selectivity of a
matchingvalue, and accumulates sslot.numbers when there is no match.                       
>  double var_eq_non_const() —   consumer  ; reads the MCV statistics and currently uses sslot.numbers[0], i.e., the
largestselectivity.                                                                        
>  double mcv_selectivity() —   consumer  ; reads the MCV list, checks each value against the comparison condition, and
accumulatesthe corresponding selectivities.                                             
>  double ineq_histogram_selectivity() —   consumer  ; reads the MCV statistics but only uses mcvslot.nnumbers.
                                                                                        
>  Selectivity booltestsel() —   consumer  ; reads the first MCV element. If the first element is true, it uses
sslot.numbers[0];otherwise, it uses 1.0 - sslot.numbers[0] - freq_null.                         
>  Datum eqjoinsel() —   consumer  ; apart from the hash algorithm, iterates through the MCV list and compares values
using"=".                                                                                 
>  void estimate_hash_bucket_stats() —   consumer  ; uses the first/largest-selectivity element by taking  mcv_freq =
sslot.numbers[0].                                                                         
>  static bool get_variable_range() —   consumer  ; iterates through the MCV list and compares values using "=".

regards,
--
ZizhuanLiu (X-MAN) 
44973863@qq.com


pgsql-hackers by date:

Previous
From: Michael Paquier
Date:
Subject: Re: DSA_ALLOC_NO_OOM vs dsm_create ERROR leaving a half-initialized pgstats hash entry
Next
From: shveta malik
Date:
Subject: Re: [PATCH] Release replication slot on error in SQL-callable slot functions