Skip to content

query_plan_join_swap_table changes the result of ANY INNER JOIN with OR-ed ON conditions (deterministic, max_threads = 1) #114354

Description

@zlareb1

Describe the bug

The planner's automatic build-side swap (query_plan_join_swap_table, default auto) changes the result of an ANY INNER JOIN with OR-ed conditions in ON. On identical single-part tables, single-threaded, the swapped plan deterministically returns 6 rows and the unswapped plan 3 rows — the swapped output is a strict superset. EXPLAIN actions = 1 shows both plans keep Type: inner | Strictness: any | Algorithm: HashJoin while the table read order flips, so the ANY deduplication is applied to opposite sides. With a single equality condition (no OR) both plans agree, so the multi-disjunct key path is the trigger.

How to reproduce

CREATE TABLE l (a Int32, b Int32) ENGINE = MergeTree ORDER BY a;
CREATE TABLE r (a Int32, b Int32) ENGINE = MergeTree ORDER BY a;
INSERT INTO l SELECT number % 8, number % 3 FROM numbers(12);
INSERT INTO r SELECT number % 5, number % 8 FROM numbers(12);

SELECT * FROM l ANY JOIN r ON l.b = r.b OR l.a = r.a ORDER BY ALL
SETTINGS max_threads = 1, query_plan_join_swap_table = 'true';   -- 6 rows

SELECT * FROM l ANY JOIN r ON l.b = r.b OR l.a = r.a ORDER BY ALL
SETTINGS max_threads = 1, query_plan_join_swap_table = 'false';  -- 3 rows

Outputs, stable across 20/20 runs per arm (clickhouse local, one part per table, max_threads = 1):

-- swap 'true' (and the default 'auto', which swaps here):
0  0  0  0
0  2  0  2
1  1  1  1
2  2  2  7
3  0  3  3
4  1  4  4

-- swap 'false':
0  0  0  0
0  2  0  2
1  1  1  1

Whatever row choice ANY is permitted to make per key, the two plans do not even agree on the number of emitted rows, and the divergence is controlled purely by a performance plan setting. Note also that both outputs emit only a small subset of the left keys that have matches (for example l = (2, 1) matches r = (4, 1) via b but is emitted by neither plan), so the intended ANY + OR semantics may be off in both plans — the deterministic swap-controlled difference is the unambiguous part.

Affected versions

Reproduced on master 26.8.1.1068 and on 26.7. Not bisected.

Related

Related: #112640 (the ANY LEFT JOIN + OR face of the swap: racy under max_threads > 1; this INNER face is deterministic and single-threaded)
Related: #111645

Found by an automatic optimizer-testing framework (differential testing of optimizer settings, query plans, and equivalent rewrites).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed user-visible misbehaviour in official releasecomp-joinsJOINs end-to-end (planning hooks + runtime join operators/algorithms). Single bucket to avoid pla...

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions