You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The filter-cost sort in pkg/sql/plan/stats.go (sortFilterListByStats / the Node_TABLE_SCAN branch) orders a scan's FilterList with a non-strict comparator:
cost1 <= cost2 is not a valid strict weak ordering (it reports "less" in both directions for equal costs). Consequences:
The relative order of equal-cost filters is undefined — it is an artifact of sort.Slice's (unstable) algorithm interacting with the non-strict predicate, not a defined priority.
That undefined order is observable, because downstream consumers depend on it:
Switching to a strict comparator (e.g. cmp.Compare(cost1, cost2), stable or not) is the desirable end state, but doing so changes the equal-cost tie order, which currently regresses correctness via #25639: vector/vector_ivf_mode then returns rows past the distance bound (7 rows instead of 3). So this cleanup is effectively blocked by #25639 — the order must stop being correctness-relevant before it can be redefined.
Give the filter-cost sort an explicit, deterministic, valid strict weak ordering (strict cmp.Compare on cost, plus an explicit tie-breaker if a defined priority among equal-cost filters is desired), and
migrate it to slices.SortFunc/SortStableFunc to match the rest of the codebase and drop the sort.Slice interface{} boxing.
Consumers of FilterList order should not rely on tie ordering for correctness (that is the #25639 half); this issue is the planner half — make the order well-defined in the first place.
Related, not duplicate. #25639 is the IVF index dropping residual predicates (order-sensitive consumer). This issue is the planner producing an ill-defined filter order (non-strict comparator). Fixing #25639 unblocks the strict/deterministic cleanup here; fixing this makes the emitted order well-defined. Both are needed for a fully robust result.
Summary
The filter-cost sort in
pkg/sql/plan/stats.go(sortFilterListByStats/ theNode_TABLE_SCANbranch) orders a scan'sFilterListwith a non-strict comparator:cost1 <= cost2is not a valid strict weak ordering (it reports "less" in both directions for equal costs). Consequences:sort.Slice's (unstable) algorithm interacting with the non-strict predicate, not a defined priority.mode=preindex probes with (→ wrong results, see IVFFLAT 'mode=pre' drops extra distance predicates (residual filter not applied) → wrong results, order-dependent #25639);test/distributed/cases/operator/is_not_operator.sql).slices.SortFunccomparator without changing behavior. This is why PR perf: replace stdlib sort.Slice with slices pkg to eliminate heap escapes #25616 (sort.Slice → slices) had to leave exactly this one site onsort.Slice.Why it can't just be made strict today
Switching to a strict comparator (e.g.
cmp.Compare(cost1, cost2), stable or not) is the desirable end state, but doing so changes the equal-cost tie order, which currently regresses correctness via #25639:vector/vector_ivf_modethen returns rows past the distance bound (7 rows instead of 3). So this cleanup is effectively blocked by #25639 — the order must stop being correctness-relevant before it can be redefined.Proposed fix (once #25639 is resolved)
cmp.Compareon cost, plus an explicit tie-breaker if a defined priority among equal-cost filters is desired), andslices.SortFunc/SortStableFuncto match the rest of the codebase and drop thesort.Sliceinterface{} boxing.Consumers of
FilterListorder should not rely on tie ordering for correctness (that is the #25639 half); this issue is the planner half — make the order well-defined in the first place.Relationship to #25639
Related, not duplicate. #25639 is the IVF index dropping residual predicates (order-sensitive consumer). This issue is the planner producing an ill-defined filter order (non-strict comparator). Fixing #25639 unblocks the strict/deterministic cleanup here; fixing this makes the emitted order well-defined. Both are needed for a fully robust result.