Uh oh!
There was an error while loading. Please reload this page.
[feature](nereids) support multi_distinct_collect_list and multi_distinct_array_agg - #65245
[feature](nereids) support multi_distinct_collect_list and multi_distinct_array_agg#65245Baymine wants to merge 3 commits into
Conversation
…inct_array_agg ### What problem does this PR solve? Issue Number: closeapache#65244 Problem Summary: When a single GROUP BY contained more than one DISTINCT aggregate and any of them was collect_list(distinct ...) or array_agg(distinct ...), planning failed with "... can't support multi distinct". CheckMultiDistinct rejects any distinct aggregate that is not a SupportMultiDistinct, and collect_list / array_agg did not implement that interface, so queries such as select g, collect_list(distinct a), collect_list(distinct b) from t group by g select g, array_agg(distinct a), array_agg(distinct b) from t group by g could not run at all. This adds the missing multi-distinct variants and wires them into the existing framework: - FE: CollectList and ArrayAgg now implement SupportMultiDistinct and convert to the new MultiDistinctCollectList / MultiDistinctArrayAgg functions. The new functions implement MultiDistinction, are registered in BuiltinAggregateFunctions, and get visitor hooks in AggregateFunctionVisitor. - BE: registers multi_distinct_collect_list and multi_distinct_array_agg. Both dedup in the aggregate state via the existing Set-backed collect data, because the Nereids multi-distinct plan does not push the distinct argument into the group-by key. ### Release note Support multiple DISTINCT aggregates in one GROUP BY when they include collect_list(distinct ...) or array_agg(distinct ...). ### Check List (For Author) - Test: - [x] Unit Test: MultiDistinctArrayAggTest, MultiDistinctCollectListTest, CheckMultiDistinctTest (28 cases, all pass). - [x] Regression test: added regression-test/suites/nereids_p0/multi_distinct/multi_distinct_collect_list.groovy (verified via CI; local cluster restart was skipped in this environment). - Behavior changed: Yes. Queries that previously failed with "can't support multi distinct" for collect_list/array_agg now plan and run. - Does this need documentation: No.
hello-stephen
commented
Jul 6, 2026
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
Baymine
commented
Jul 6, 2026
run buildall |
hello-stephen
commented
Jul 6, 2026
FE UT Coverage ReportIncrement line coverage |
morrySnow
left a comment
There was a problem hiding this comment.
Review Summary
This PR adds multi_distinct_collect_list and multi_distinct_array_agg support — a solid feature enabling queries with multiple DISTINCT aggregates in one GROUP BY that include collect_list/array_agg. The overall approach (FE: SupportMultiDistinct + MultiDistinction classes; BE: registering new function names) follows existing patterns. Good test coverage.
Below are the issues found, ranked by severity:
1. [HIGH] array_agg distinct path silently drops nulls
See inline comment in aggregate_function_array_agg.cpp. The Set-based dedup data structure cannot represent null elements, and the null-skipping adapter drops them before the inner function sees them. This means array_agg(distinct nullable_col) returns different results when it takes the multi-distinct path vs. the single-distinct path.
2. [MEDIUM] canSkewRewrite can produce nested ARRAY<ARRAY>
Adding SupportMultiDistinct to ArrayAgg/CollectList makes canSkewRewrite() (Aggregate.java:~206) return true. In SplitAggMultiPhase.java:~334, phase 3 calls getAggregateFunction() which falls to the else branch: aggFunc.withDistinctAndChildren(false, children). This wraps the already-array intermediate slot in another array_agg(), producing ARRAY<ARRAY<T>>. A query with a skew hint on these functions would return deeply nested arrays. Consider adding a guard in canSkewRewrite() or handling array concatenation in the skew phases.
3. [MEDIUM] DistinctWindowExpression doesn't handle ArrayAgg/CollectList
DistinctWindowExpression.java:87-101 has hardcoded instanceof checks for Count, Sum, GroupConcat — it never queries SupportMultiDistinct. ARRAY_AGG(DISTINCT col) OVER(...) or COLLECT_LIST(DISTINCT col) OVER(...) would not be converted. Consider refactoring to use the SupportMultiDistinct interface like other rules do.
4. [LOW] get_name() returns "collect_set" for multi_distinct_* functions
aggregate_function_collect.h:410-416: get_name() returns "collect_set" for all Set-based data. Three different functions (collect_set, multi_distinct_collect_list, multi_distinct_array_agg) all report as "collect_set" in EXPLAIN ANALYZE profiles and error messages, making debugging confusing.
5. [LOW] Unused #include
See inline comment in aggregate_function_array_agg.cpp.
6. [LOW] convertToMultiDistinct silently drops limit argument
See inline comment in CollectList.java.
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 6, 2026
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
hello-stephen
commented
Jul 6, 2026
FE Regression Coverage ReportIncrement line coverage |
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.
Uh oh!
There was an error while loading. Please reload this page.
### What problem does this PR solve? Issue Number: closeapache#65244 Problem Summary: Follow-up fixes for the multi_distinct_collect_list / multi_distinct_array_agg review: 1. array_agg(distinct) dropped NULLs on the multi-distinct path. The Set-backed collect data cannot store a null and the not-nullable wrapper skipped null rows, so array_agg(distinct nullable_col) returned a different result than the single-distinct path (which keeps one NULL). The multi_distinct_array_agg path now feeds the nullable column in directly and records whether a NULL was seen (has_null), emitting a single NULL element on output, matching array_agg semantics. collect_set / multi_distinct_collect_list keep ignoring NULLs. 2. Skew rewrite could produce ARRAY<ARRAY<T>>. Aggregate.canSkewRewrite() now excludes array-returning aggregates, so array_agg/collect_list with a skew hint fall back to the normal multi-distinct plan instead of re-wrapping the array intermediate in another array_agg. 3. DISTINCT array_agg/collect_list as window functions were not converted. DistinctWindowExpression now uses the SupportMultiDistinct interface instead of hardcoded instanceof checks, so ARRAY_AGG(DISTINCT ..) OVER(..) and COLLECT_LIST(DISTINCT ..) OVER(..) are rewritten like count/sum/group_concat. 4. get_name() reported "collect_set" for every Set-backed collect function. The real function name is now threaded into AggregateFunctionCollect so collect_set / multi_distinct_collect_list / multi_distinct_array_agg report distinct names in EXPLAIN / profiles. 5. Removed the unused #include "common/status.h" from aggregate_function_array_agg.cpp. 6. FE and BE advertised different type support: MultiDistinctArrayAgg / MultiDistinctCollectList accept AnyDataType, but the Set-backed BE path only handles scalar/string element types and would raise an internal error for complex/object types. SupportMultiDistinct.checkSupportMultiDistinct() (called from CheckMultiDistinct when the multi-distinct rewrite is required) now rejects those types during FE analysis with a user-facing error. Single distinct (no rewrite) still supports complex types as before. ### Release note None. ### Check List (For Author) - Test: - [x] Unit Test: FE MultiDistinctArrayAggTest (9), MultiDistinctCollectListTest (12), CheckMultiDistinctTest (10, incl. complex-type rejection) all pass. BE VAggCollectTest adds test_multi_distinct_array_agg_preserves_null (NULL preservation add/serialize/deserialize/merge round trip) and test_multi_distinct_functions_are_registered; all 6 pass. - [x] Regression test: extended regression-test/suites/nereids_p0/multi_distinct/multi_distinct_collect_list.groovy with NULL-handling, DISTINCT window-function, and complex-type rejection cases. - Behavior changed: Yes. array_agg(distinct nullable) on the multi-distinct path preserves a NULL element; DISTINCT array_agg/collect_list are usable as window functions; complex element types are rejected in FE for the multi-distinct rewrite instead of failing at BE runtime. - Does this need documentation: No.
Baymine
commented
Jul 7, 2026
run buildall |
hello-stephen
commented
Jul 7, 2026
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
Baymine
commented
Jul 13, 2026
run buildall |
hello-stephen
commented
Jul 13, 2026
TPC-H: Total hot run time: 29647 ms |
hello-stephen
commented
Jul 13, 2026
TPC-DS: Total hot run time: 180004 ms |
hello-stephen
commented
Jul 13, 2026
ClickBench: Total hot run time: 25.09 s |
hello-stephen
commented
Jul 13, 2026
FE Regression Coverage ReportIncrement line coverage |
hello-stephen
commented
Jul 13, 2026
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
feiniaofeiafei
commented
Jul 24, 2026
The issue mentioned in this PR—where the two SQL statements select g, collect_list(distinct a), collect_list(distinct b) from t group by g and select g, array_agg(distinct a), array_agg(distinct b) from t group by g could not be executed—has been resolved after the merge of #65664. This PR implements a different approach to enable execution, using CTE (Common Table Expression) splitting to achieve the desired functionality. |
What problem does this PR solve?
Issue Number: close#65244
Problem Summary:
When a single GROUP BY contained more than one DISTINCT aggregate and any of
them was collect_list(distinct ...) or array_agg(distinct ...), planning failed
with "... can't support multi distinct". CheckMultiDistinct rejects any distinct
aggregate that is not a SupportMultiDistinct, and collect_list / array_agg did
not implement that interface, so queries such as
could not run at all.
This adds the missing multi-distinct variants and wires them into the existing
framework:
the new MultiDistinctCollectList / MultiDistinctArrayAgg functions. The new
functions implement MultiDistinction, are registered in
BuiltinAggregateFunctions, and get visitor hooks in AggregateFunctionVisitor.
dedup in the aggregate state via the existing Set-backed collect data, because
the Nereids multi-distinct plan does not push the distinct argument into the
group-by key.
Release note
Support multiple DISTINCT aggregates in one GROUP BY when they include
collect_list(distinct ...) or array_agg(distinct ...).
Check List (For Author)