From be6ebc0b9115cf09c40ee0778bd430df869d349f Mon Sep 17 00:00:00 2001 From: "ZizhuanLiu(X-MAN)" <44973863@qq.com> Date: Sat, 12 Sep 2026 09:45:04 +0800 Subject: [PATCH v6] Fix var_eq_const: sum selectivity of all matching MCV entries for nondeterministic collation When evaluating selectivity for var_eq_const(), if the query uses a nondeterministic collation which differs from the statistics collation, a single constant may match multiple entries in the MCV list. The original code would stop at the first matched MCV entry and return its frequency, leading to underestimated selectivity and poor-quality query plans. Enable full-MCV-array scan under the correct condition: the collation used for the comparison is nondeterministic and differs from the statistics collation. When full scan is enabled, iterate over all MCV entries and accumulate the total selectivity of every matching entry. Keep the fast-path early-break for all other cases to avoid performance regression. Discussion: https://www.postgresql.org/message-id/flat/tencent_A15A9D89A86F2E4B086ABA462578B9B64307@qq.com Commitfest: https://commitfest.postgresql.org/patch/7075/ --- src/backend/utils/adt/selfuncs.c | 57 ++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index e27ec9e5..3500055e 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -413,6 +413,7 @@ var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation, AttStatsSlot sslot; bool match = false; int i; + double sumcommon = 0.0; /* * Is the constant "=" to any of the column's most common values? @@ -427,6 +428,7 @@ var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation, { LOCAL_FCINFO(fcinfo, 2); FmgrInfo eqproc; + bool scan_entire_mcv = false; fmgr_info(opfuncoid, &eqproc); @@ -446,6 +448,33 @@ var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation, else fcinfo->args[0].value = constval; + /* + * Scanning the entire MCV array is needed when the collation used + * for the comparison is nondeterministic and differs from the + * statistics collation. In this case, the comparison may match + * multiple MCV values, so we must continue scanning after finding + * a match. + */ + if (sslot.stacoll != collation && OidIsValid(collation)) + { + pg_locale_t mylocale = pg_newlocale_from_collation(collation); + + scan_entire_mcv = !mylocale->deterministic; + } + + /* + * Compare the constant expression with the MCVs. + * + * If the constant matches the current MCV, stop here when a full + * MCV scan is not required. Otherwise, continue scanning the + * remaining MCVs and accumulate the selectivity of all matching + * MCVs. + * + * While scanning, also accumulate the selectivity of the MCVs + * examined so far. This is used later to estimate the selectivity + * of a non-NULL constant that does not match any MCV. + */ + selec = 0.0; for (i = 0; i < sslot.nvalues; i++) { Datum fresult; @@ -458,9 +487,22 @@ var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation, fresult = FunctionCallInvoke(fcinfo); if (!fcinfo->isnull && DatumGetBool(fresult)) { + /* + * Constant is "=" to this common value. We know + * selectivity exactly (or as exactly as ANALYZE could + * calculate it, anyway). + */ match = true; - break; + if (!scan_entire_mcv) + { + selec = sslot.numbers[i]; + break; + } + + selec += sslot.numbers[i]; } + + sumcommon += sslot.numbers[i]; } } else @@ -469,26 +511,15 @@ var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation, i = 0; /* keep compiler quiet */ } - if (match) - { - /* - * Constant is "=" to this common value. We know selectivity - * exactly (or as exactly as ANALYZE could calculate it, anyway). - */ - selec = sslot.numbers[i]; - } - else + if (!match) { /* * Comparison is against a constant that is neither NULL nor any * of the common values. Its selectivity cannot be more than * this: */ - double sumcommon = 0.0; double otherdistinct; - for (i = 0; i < sslot.nnumbers; i++) - sumcommon += sslot.numbers[i]; selec = 1.0 - sumcommon - nullfrac; CLAMP_PROBABILITY(selec); -- 2.43.0