Background
Found while adversarially reviewing #1663 (the fix for #1659, range-join le_lt/ge_gt selection).
_helpers._separate_conditions_based_on_op produces mapping["le_lt"]/["ge_gt"]/["le_or_ge"], consumed by two different dispatch trees:
Confirmed empirically (during #1663's review): a query combining grp ==, a > lo, and two <-type candidates with real, measured selectivity skew dispatches through _equi_range_join._get_indices with le_lt still bound to the unswapped, less-selective candidate.
Why this matters
This isn't a new inconsistency - _equi_not_range_join.py's gap predates #1658 and #1663 just didn't close the equi-adjacent copies either, for the same reason (each fix was scoped to the non-equi dispatch tree it touched). But it means none of #1657/#1658/#1659/#1663's fixes actually apply once an == predicate is combined with the inequality/range predicates - and an id/category equality plus a date/numeric range is a very ordinary join shape, arguably more common than a pure non-equi join in practice. The underlying pathology is the same one #1641/#1658/#1659 already measured (up to ~77x for same-direction, ~7-11x for range joins, worse again under join_algorithm="regions") - it just isn't fixed for this dispatch tree yet.
Correctness
Same invariance argument as #1641/#1658/#1659 applies unchanged: _equi_range_join.py/_equi_uniq_join.py still process ge_gt before le_lt structurally regardless of which candidate occupies either slot, so picking independently per bound (or per same-direction anchor) can't produce wrong output here either - this is a missed-optimization gap, not a latent correctness risk.
Ask
Extend selectivity-aware selection to the _get_indices_equi.py dispatch tree:
Both fixes already exist and are algorithm-agnostic at the point they're called (after null-stripping, before dispatch) - this should mostly be a matter of calling them from _get_indices_equi.py's own null-stripping point too, not new selection logic.
Related
Background
Found while adversarially reviewing #1663 (the fix for #1659, range-join
le_lt/ge_gtselection)._helpers._separate_conditions_based_on_opproducesmapping["le_lt"]/["ge_gt"]/["le_or_ge"], consumed by two different dispatch trees:_get_indices_non_equi.py- pure non-equi joins. This is what [PERF] Selectivity-aware anchor predicate selection for conditional_join #1658 (same-directionle_or_ge, via_select_anchor) and [PERF] Selective bound selection for range joins #1663 (range-joinle_lt/ge_gt, via_maybe_select_better_range_bounds) both fixed._get_indices_equi.py- joins combining an==predicate with non-equi predicates. Neither fix reaches this path:_equi_uniq_join.pyand_equi_range_join.pyconsumemapping["le_lt"]/["ge_gt"]directly (still first-encountered, never selectivity-aware), and_equi_not_range_join.pyconsumesmapping["le_or_ge"]the same pre-[PERF] Selectivity-aware anchor predicate selection for conditional_join #1658 way ((l1_col, r1_col, op), *rest = mapping["le_or_ge"], no_select_anchorcall at all).Confirmed empirically (during #1663's review): a query combining
grp ==,a > lo, and two<-type candidates with real, measured selectivity skew dispatches through_equi_range_join._get_indiceswithle_ltstill bound to the unswapped, less-selective candidate.Why this matters
This isn't a new inconsistency -
_equi_not_range_join.py's gap predates #1658 and #1663 just didn't close the equi-adjacent copies either, for the same reason (each fix was scoped to the non-equi dispatch tree it touched). But it means none of #1657/#1658/#1659/#1663's fixes actually apply once an==predicate is combined with the inequality/range predicates - and an id/category equality plus a date/numeric range is a very ordinary join shape, arguably more common than a pure non-equi join in practice. The underlying pathology is the same one #1641/#1658/#1659 already measured (up to ~77x for same-direction, ~7-11x for range joins, worse again underjoin_algorithm="regions") - it just isn't fixed for this dispatch tree yet.Correctness
Same invariance argument as #1641/#1658/#1659 applies unchanged:
_equi_range_join.py/_equi_uniq_join.pystill processge_gtbeforele_ltstructurally regardless of which candidate occupies either slot, so picking independently per bound (or per same-direction anchor) can't produce wrong output here either - this is a missed-optimization gap, not a latent correctness risk.Ask
Extend selectivity-aware selection to the
_get_indices_equi.pydispatch tree:_equi_uniq_join.py/_equi_range_join.py: reuse [PERF] Selective bound selection for range joins #1663's_maybe_select_better_range_bounds(or hoist it somewhere both dispatch trees can call) for thele_lt/ge_gtcase._equi_not_range_join.py: reuse [PERF] Selectivity-aware anchor predicate selection for conditional_join #1658's_select_anchorfor thele_or_gecase.Both fixes already exist and are algorithm-agnostic at the point they're called (after null-stripping, before dispatch) - this should mostly be a matter of calling them from
_get_indices_equi.py's own null-stripping point too, not new selection logic.Related