Uh oh!
There was an error while loading. Please reload this page.
[fix](fd)drop Function dependencies from join outer side - #65982
Conversation
hello-stephen
commented
Jul 24, 2026
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
englefly
commented
Jul 24, 2026
run buildall |
englefly
commented
Jul 24, 2026
/review |
There was a problem hiding this comment.
Request changes: one P2 optimizer performance regression remains.
Critical checkpoint conclusions:
- Goal and correctness: the patch prevents unsafe direct functional dependencies from surviving a null-extended outer-join side, and no unsafe edge is reconstructed through unique, uniform, equality, transitive, or pruning paths. However, it also drops still-valid dependencies whose determinant contains an originally non-null child slot; see the inline finding.
- Scope and parallel paths: all 14 current join types, ASOF directionality, semi/anti/NAAJ and mark shapes were checked. The logical and physical hash-join switches agree; nested-loop recomputation blocks FD propagation. No additional path-specific issue was found.
- Tests: the changed FdTest assertions inspect only the logical plan and use NOT NULL determinants, so they codify the safe dependency loss rather than reproduce the nullable-determinant collision. The physical reset/recomputation path is not covered.
- Performance: the accepted issue prevents EliminateGroupByKey and EliminateOrderByKey from applying valid reductions. No other CPU, memory, or hot-path concern was found.
- Concurrency, lifecycle, configuration, compatibility, persistence/transactions, data writes, FE-BE propagation, and observability: not involved in this change.
- User focus: no additional focus was provided; the complete PR was reviewed.
Validation was static only. No build or test was run because the governing review prompt prohibits builds in this environment.
Uh oh!
There was an error while loading. Please reload this page.
hello-stephen
commented
Jul 24, 2026
TPC-H: Total hot run time: 23165 ms |
hello-stephen
commented
Jul 24, 2026
TPC-DS: Total hot run time: 117085 ms |
hello-stephen
commented
Jul 24, 2026
ClickBench: Total hot run time: 18.65 s |
PR approved by at least one committer and no changes requested. |
englefly
commented
Jul 29, 2026
run buildall |
englefly
commented
Jul 29, 2026
/review |
There was a problem hiding this comment.
Request changes: one P1 correctness issue and two distinct P2 FD-preservation gaps remain. The existing thread also remains relevant; no duplicate inline was added for its composite-determinant and test-coverage points.
Critical checkpoint conclusions:
- Goal and correctness: the PR aims to retain exactly the functional dependencies that survive outer-join null extension.
MAIN-F1shows the implementation can retain an invalid historical-slot FD and letEliminateGroupByKeymerge distinct groups.MAIN-F2andMAIN-F3show it also drops safe transitive and null-rejected-key dependencies. - Scope and clarity: the five-file change is focused. Both logical and physical hash-join switches cover all 14 current join types with matching side propagation; no unrelated source change was found.
- Concurrency and lifecycle: no runtime concurrency, locks, shared mutable state, or static initialization are involved. The relevant optimizer-property lifecycle was traced through lazy logical traits and bottom-up physical recomputation; historical
Slotnullability in that lifecycle is the P1 issue. - Parallel and special paths: ordinary/right/full/ASOF outer joins, semi/anti/NAAJ, mark joins, implementation conversion, output pruning, and the FD-blocking nested-loop path were checked.
MAIN-F3is intentionally limited to a proven null-rejecting key on the non-preserved side of a one-sided join; full outer and null-safe/unproven predicates are excluded. - Tests: the positive logical assertions cover direct schema-non-null determinants. The new nullable-key negative assertions are vacuous because that scan never seeds the claimed FD, and no added test reaches physical recomputation; those points are already covered by the existing thread and were not duplicated inline.
- Configuration, compatibility, persistence, transactions, data writes, FE-BE propagation, error boundaries, and observability: not involved.
- Performance: graph copying remains linear, but
MAIN-F2andMAIN-F3prevent valid group/order-key reductions. - User focus: no additional review focus was provided; the whole PR was reviewed.
- Completion: three bounded rounds converged, with every final-round full and risk reviewer returning
NO_NEW_VALUABLE_FINDINGSagainst this exact comment set.
Validation was static only. No build or test was run because the governing review prompt prohibits builds in this environment.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
hello-stephen
commented
Jul 29, 2026
TPC-H: Total hot run time: 29678 ms |
hello-stephen
commented
Jul 29, 2026
FE UT Coverage ReportIncrement line coverage |
hello-stephen
commented
Jul 29, 2026
TPC-DS: Total hot run time: 177247 ms |
hello-stephen
commented
Jul 29, 2026
ClickBench: Total hot run time: 24.96 s |
englefly
commented
Aug 4, 2026
run buildall |
hello-stephen
commented
Aug 4, 2026
FE UT Coverage ReportIncrement line coverage |
hello-stephen
commented
Aug 4, 2026
TPC-H: Total hot run time: 28424 ms |
hello-stephen
commented
Aug 4, 2026
TPC-DS: Total hot run time: 166787 ms |
hello-stephen
commented
Aug 4, 2026
ClickBench: Total hot run time: 23.81 s |
hello-stephen
commented
Aug 4, 2026
FE Regression Coverage ReportIncrement line coverage |
englefly
commented
Aug 6, 2026
run external |
hello-stephen
commented
Aug 6, 2026
FE Regression Coverage ReportIncrement line coverage |
Uh oh!
There was an error while loading. Please reload this page.
### What problem does this PR solve?
Issue Number: N/A (no issue linked)
Related PR: N/A
Problem Summary:
Nereids derives functional dependencies (FDs) from each operator's
children via `DataTrait`, and rewrite rules such as
`EliminateGroupByKey`, `EliminateGroupByKeyByUniform`,
`EliminateOrderByKey` and `ConstantPropagation` consume these FDs to
drop functionally-determined grouping/ordering keys. If an FD is derived
incorrectly, those rules may produce wrong query results.
`LogicalJoin.computeFd()` and `PhysicalHashJoin.computeFd()` previously
propagated FDs from both join inputs, only excluding the semi/anti-join
side:
```java
if (!joinType.isLeftSemiOrAntiJoin()) {
builder.addFuncDepsDG(right().getLogicalProperties().getTrait());
}
if (!joinType.isRightSemiOrAntiJoin()) {
builder.addFuncDepsDG(left().getLogicalProperties().getTrait());
}
```
For outer joins the nullable side is null-extended: unmatched rows are
padded with NULLs, which invalidates FDs from that side. For example, in
`t1 LEFT OUTER JOIN t2`, if the right side has the FD `t2.a -> t2.b` and
`a` is nullable, a matched row with `a = NULL, b = 1` and an unmatched
row `(a = NULL, b = NULL)` together violate `a -> b` on the join output.
The old code still propagated such FDs from the nullable side for `LEFT
OUTER JOIN` (right side), `RIGHT OUTER JOIN` (left side) and `FULL OUTER
JOIN` (both sides), so a downstream rule could remove a group-by key
that is not actually functionally determined and change the query
result.
This PR fixes the FD derivation on join outputs:
1. `computeFd()` in `LogicalJoin` and `PhysicalHashJoin` is rewritten
with an explicit switch over join types:
- inner / cross joins: propagate FDs from both sides;
- semi / anti joins: propagate FDs only from the output side;
- outer joins: propagate FDs from the preserved side, and from the
nullable side only the FDs whose determinant is NOT NULL in the child —
matched rows then always carry a non-null determinant, so they cannot
collide with the `(NULL, NULL)` null-extension of unmatched rows;
- full outer join: keep only the NOT-NULL-determinant FDs from both
sides.
2. A new `DataTrait.Builder.addFuncDepsDGForOuterJoinNullableSide()` /
`FuncDepsDG.Builder.addDepsForOuterJoinNullableSide()` implements the
NOT-NULL-determinant filter.
3. The nullability check is performed against the *current* child output
rather than the slot stored in the FD graph: slots are keyed by ExprId
and may carry a stale `nullable` flag (e.g. after
`LogicalSubQueryAliasToLogicalProject` inlining), so a determinant that
became nullable in the immediate child is dropped.
Tests in `FdTest` are updated (FOJ/LOJ/ROJ no longer propagate
nullable-side FDs, while NOT-NULL-determinant FDs from the nullable side
are kept), and a new `testNestedOuterJoinNullableDeterminant` covers the
nested outer-join case where the determinant's stale non-nullable flag
must not leak through, verified on both the logical and the physical
join paths.
### Release note
None
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [ ] Regression test
- [x] Unit Test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- Behavior changed:
- [x] No.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [x] No.
- [ ] Yes. <!-- Add document PR link here. eg:
apache/doris-website#1214 -->
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->…rapping (#64849) ### What problem does this PR solve? When a group-by key is functionally dependent on another key (e.g. s_suppkey -> s_name via PK) but required in output, remove it from GROUP BY and wrap with ANY_VALUE(). Previously EliminateGroupByKey kept such keys in GROUP BY to preserve SQL semantics. Now they are replaced with ANY_VALUE wrappers in the output, allowing the group-by set to be minimized while keeping the column in SELECT. Public findCanBeRemovedExpressions() API preserved for backward compatibility. Internal logic split into FindResult with separate removeExpression and wrapWithAnyValue sets. Test: testEliminateByPkWithOutputNeeded verifies ANY_VALUE wrapping when SELECT contains an FD-redundant group-by key. Issue Number: close #xxx Related PR: #65982#66801#66803 上面 3 个 pr 是原有 master 的bug fix. pick 这个 pr 前, 确保上面 3 个 pr 已经 pick Problem Summary: ### Release note None ### Check List (For Author) - Test <!-- At least one of them must be included. --> - [ ] Regression test - [ ] 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? --> - Behavior changed: - [ ] No. - [ ] Yes. <!-- Explain the behavior change --> - Does this need documentation? - [ ] No. - [ ] Yes. <!-- Add document PR link here. eg: apache/doris-website#1214 --> ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label <!-- Add branch pick label that this PR should merge into -->
What problem does this PR solve?
Issue Number: N/A (no issue linked)
Related PR: N/A
Problem Summary:
Nereids derives functional dependencies (FDs) from each operator's children via
DataTrait, and rewrite rules such asEliminateGroupByKey,EliminateGroupByKeyByUniform,EliminateOrderByKeyandConstantPropagationconsume these FDs to drop functionally-determined grouping/ordering keys. If an FD is derived incorrectly, those rules may produce wrong query results.LogicalJoin.computeFd()andPhysicalHashJoin.computeFd()previously propagated FDs from both join inputs, only excluding the semi/anti-join side:For outer joins the nullable side is null-extended: unmatched rows are padded with NULLs, which invalidates FDs from that side. For example, in
t1 LEFT OUTER JOIN t2, if the right side has the FDt2.a -> t2.bandais nullable, a matched row witha = NULL, b = 1and an unmatched row(a = NULL, b = NULL)together violatea -> bon the join output. The old code still propagated such FDs from the nullable side forLEFT OUTER JOIN(right side),RIGHT OUTER JOIN(left side) andFULL OUTER JOIN(both sides), so a downstream rule could remove a group-by key that is not actually functionally determined and change the query result.This PR fixes the FD derivation on join outputs:
computeFd()inLogicalJoinandPhysicalHashJoinis rewritten with an explicit switch over join types:(NULL, NULL)null-extension of unmatched rows;DataTrait.Builder.addFuncDepsDGForOuterJoinNullableSide()/FuncDepsDG.Builder.addDepsForOuterJoinNullableSide()implements the NOT-NULL-determinant filter.nullableflag (e.g. afterLogicalSubQueryAliasToLogicalProjectinlining), so a determinant that became nullable in the immediate child is dropped.Tests in
FdTestare updated (FOJ/LOJ/ROJ no longer propagate nullable-side FDs, while NOT-NULL-determinant FDs from the nullable side are kept), and a newtestNestedOuterJoinNullableDeterminantcovers the nested outer-join case where the determinant's stale non-nullable flag must not leak through, verified on both the logical and the physical join paths.Release note
None
Check List (For Author)
Test
Behavior changed:
Does this need documentation?
Check List (For Reviewer who merge this PR)