Skip to content

[fix](fe) Backport runtime filter outer join fix to 4.0#64157

Open
BiteTheDDDDt wants to merge 1 commit into
apache:branch-4.0from
BiteTheDDDDt:codex/pick-64102-branch-4.0
Open

[fix](fe) Backport runtime filter outer join fix to 4.0#64157
BiteTheDDDDt wants to merge 1 commit into
apache:branch-4.0from
BiteTheDDDDt:codex/pick-64102-branch-4.0

Conversation

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Related PR: #64102

Problem Summary:

Backport #64102 to branch-4.0. The fix prevents unsafe runtime filter pushdown through the null-generating side of outer joins. The implementation was adapted to the branch-4.0 RuntimeFilterPushDownVisitor structure.

Release note

None

Check List (For Author)

  • Test:
    • Unit Test: ./run-fe-ut.sh --run org.apache.doris.nereids.postprocess.RuntimeFilterTest#testDoNotPushDownNonNullPropagatingRuntimeFilterThroughOuterJoin,org.apache.doris.nereids.postprocess.RuntimeFilterTest#testPushDownNullPropagatingRuntimeFilterThroughOuterJoin
  • Behavior changed: No
  • Does this need documentation: No

Copilot AI review requested due to automatic review settings June 5, 2026 11:49
@BiteTheDDDDt BiteTheDDDDt requested a review from morningman as a code owner June 5, 2026 11:49
@hello-stephen
Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

run buildall

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Backports the runtime filter outer-join safety fix from #64102 onto branch-4.0, preventing runtime filters from being pushed into the null-generating side of outer joins when the probe expression can turn NULL into a non-NULL value (e.g. coalesce), which can otherwise change query results.

Changes:

  • Add outer-join child-side gating in RuntimeFilterPushDownVisitor based on whether the probe expression is NULL-propagating.
  • Add FE unit tests covering both the blocked (non-NULL-propagating) and allowed (NULL-propagating) pushdown cases.
  • Add a regression test reproducing the incorrect-result scenario and validating the expected empty result.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
regression-test/suites/correctness_p0/test_runtime_filter_outer_join_nullable_side.groovy Adds a regression query reproducing the outer-join nullable-side runtime filter issue and validates result correctness.
regression-test/data/correctness_p0/test_runtime_filter_outer_join_nullable_side.out Records the expected shape plan and empty result for the new regression test.
fe/fe-core/src/test/java/org/apache/doris/nereids/postprocess/RuntimeFilterTest.java Adds unit tests verifying pushdown is blocked for coalesce(...) and allowed for direct slot equality through outer joins.
fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/RuntimeFilterPushDownVisitor.java Implements the null-generating-side pushdown restriction unless the probe expression is NULL-propagating.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@BiteTheDDDDt BiteTheDDDDt force-pushed the codex/pick-64102-branch-4.0 branch from 3b1eb64 to 7784789 Compare June 6, 2026 02:15
@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen
Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 14.81% (4/27) 🎉
Increment coverage report
Complete coverage report

…pache#64102)

related with: apache#57425

Runtime filters from a parent inner join could be pushed through an
outer join into the null-generating child even when the probe expression
was not null-propagating for that child.

The problem can be reproduced with this SQL shape:

```sql
create table rf_outer_join_nullable_a (pk int)
duplicate key(pk)
distributed by hash(pk) buckets 1
properties("replication_num" = "1");

create table rf_outer_join_nullable_b (pk int)
duplicate key(pk)
distributed by hash(pk) buckets 1
properties("replication_num" = "1");

create table rf_outer_join_nullable_c (pk int)
duplicate key(pk)
distributed by hash(pk) buckets 1
properties("replication_num" = "1");

insert into rf_outer_join_nullable_a values (1);
insert into rf_outer_join_nullable_b values (1);
insert into rf_outer_join_nullable_c values (0);

set disable_join_reorder = true;

select coalesce(b.pk, 0) as k, count(*) as cnt
from rf_outer_join_nullable_a a
left join rf_outer_join_nullable_b b on a.pk = b.pk
inner join rf_outer_join_nullable_c c on coalesce(b.pk, 0) = c.pk
group by 1
order by 1;
```

The correct result is empty. `a.pk = 1` matches `b.pk = 1` in the left
outer join, then the parent inner join evaluates `coalesce(1, 0) = 0`,
which is false.

The wrong plan generated a runtime filter from the parent inner join,
effectively `c.pk -> coalesce(b.pk, 0)`, and pushed it through the lower
`LEFT OUTER JOIN` into the right side scan of `b`. If `b.pk = 1` is
pre-filtered before the left outer join, the join emits a NULL-extended
row for `b`; then `coalesce(NULL, 0) = 0` becomes true and incorrectly
returns `(0, 1)`.

Therefore the runtime filter `c.pk -> coalesce(b.pk, 0)` must not be
planned on the null-generating side of the lower outer join. This PR
blocks runtime filter pushdown through an outer join's null-generating
child unless the probe expression preserves NULL semantics for slots
from that child. Normal pushdown through preserved sides and
null-propagating expressions is kept unchanged.

The bug became observable after apache#57425 changed the target lookup for
expression runtime filters from `ctx.probeExpr` to `ctx.probeSlot`.
Before that change, an expression such as `coalesce(b.pk, 0)` could not
resolve the target relation in this path and the unsafe pushdown was not
generated.

None

- Test <!-- At least one of them must be included. -->
    - [x] Regression test
    - [x] Unit Test
    - [ ] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
        - [ ] Previous test can cover this change.
        - [ ] No code files have been changed.
        - [ ] Other reason <!-- Add your reason?  -->

Added regression case with `disable_join_reorder`, `qt_shape`, and empty
result verification:

`regression-test/suites/correctness_p0/test_runtime_filter_outer_join_nullable_side.groovy`

Unit test:

`./run-fe-ut.sh --run
org.apache.doris.nereids.postprocess.RuntimeFilterTest#testDoNotPushDownNonNullPropagatingRuntimeFilterThroughOuterJoin,org.apache.doris.nereids.postprocess.RuntimeFilterTest#testPushDownNullPropagatingRuntimeFilterThroughOuterJoin`

The SQL regression case was not run locally against the available 9333
cluster because that cluster was the unpatched repro cluster.

- Behavior changed:
    - [ ] No.
- [x] Yes. Runtime filters are no longer pushed through an outer join
into its null-generating child when the probe expression can convert
NULL to a non-NULL value.

- Does this need documentation?
    - [x] No.
- [ ] Yes. <!-- Add document PR link here. eg:
apache/doris-website#1214 -->

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->

(cherry picked from commit 691189a)
@BiteTheDDDDt BiteTheDDDDt force-pushed the codex/pick-64102-branch-4.0 branch from 7784789 to 7b0ac7d Compare June 7, 2026 01:53
@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

run buildall

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants