From fdc4602992328e75aefc1f5654f1ea2b998bd1ce Mon Sep 17 00:00:00 2001 From: "ZizhuanLiu(X-MAN)" <44973863@qq.com> Date: Thu, 10 Sep 2026 20:45:45 +0800 Subject: [PATCH v5] 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 | 56 ++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index e27ec9e5..0bd313c8 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,21 @@ 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 +510,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