Skip to content

[improvement](eager-agg) Push down distinct aggregation using statistics#65099

Open
feiniaofeiafei wants to merge 1 commit into
apache:masterfrom
feiniaofeiafei:remove-cost-based-distinct-push-down
Open

[improvement](eager-agg) Push down distinct aggregation using statistics#65099
feiniaofeiafei wants to merge 1 commit into
apache:masterfrom
feiniaofeiafei:remove-cost-based-distinct-push-down

Conversation

@feiniaofeiafei

@feiniaofeiafei feiniaofeiafei commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: The distinct aggregation pushdown used a separate cost-based rewrite and supported only one distinct aggregate. Move pure distinct aggregation into eager aggregation so statistics decide whether to deduplicate below joins. Support multiple SUM/COUNT distinct aggregates with identical keys and multi-column COUNT DISTINCT while keeping the upper distinct aggregate unchanged. Queries containing non-distinct aggregates or different distinct keys are not rewritten.

Release note

Use statistics-driven eager aggregation for pure same-key distinct aggregates across joins.

Check List (For Author)

  • Test

    • 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
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: The distinct aggregation pushdown used a separate cost-based rewrite and supported only one distinct aggregate. Move pure distinct aggregation into eager aggregation so statistics decide whether to deduplicate below joins. Support multiple SUM/COUNT distinct aggregates with identical keys and multi-column COUNT DISTINCT while keeping the upper distinct aggregate unchanged. Queries containing non-distinct aggregates or different distinct keys are not rewritten.

### Release note

Use statistics-driven eager aggregation for pure same-key distinct aggregates across joins.

### Check List (For Author)

- Test: Unit Test
    - ./run-fe-ut.sh --run org.apache.doris.nereids.rules.rewrite.eageraggregation.EagerAggRewriterTest
- Behavior changed: Yes. Eligible distinct aggregations can now be deduplicated below joins according to eager aggregation statistics.
- Does this need documentation: No
@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?

@feiniaofeiafei feiniaofeiafei changed the title [improvement](fe) Push down distinct aggregation using statistics [improvement](eager-agg) Push down distinct aggregation using statistics Jul 1, 2026
@feiniaofeiafei

Copy link
Copy Markdown
Contributor Author

run buildall

@morrySnow

Copy link
Copy Markdown
Contributor

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review completed. I found one blocking compatibility issue around the removed Nereids rule enum.

Critical checkpoints:

  • Goal: the PR moves distinct aggregate pushdown into eager aggregation and broadens same-key distinct handling, but it does not preserve the old user-addressable rule name.
  • Scope/focus: the code change is otherwise focused on FE/Nereids rewrite wiring, eager aggregation, and the associated unit test.
  • Concurrency/lifecycle: no new concurrent state, locks, persistence, or lifecycle-sensitive objects were introduced.
  • Configuration/compatibility: blocking issue found. Existing disable_nereids_rules / SET_VAR / rule-hint usage can still name the removed enum and now fails through RuleType.valueOf.
  • Parallel paths: reviewed rewrite order, the removed cost-based distinct rule, eager aggregation, distinct aggregate rewrite, and same-key/multi-column distinct paths.
  • Tests: new FE unit coverage was added, but I could not run ./run-fe-ut.sh --run org.apache.doris.nereids.rules.rewrite.eageraggregation.EagerAggRewriterTest in this checkout because thirdparty/installed and thirdparty/installed/bin/protoc are missing.
  • User focus: no additional user-provided focus points were supplied.

Subagent conclusions: optimizer-rewrite reported NO_NEW_VALUABLE_FINDINGS. tests-session-config reported TEST-1, which was merged as a duplicate of MAIN-1 and became the single inline comment. Convergence round 1 ended with both live subagents reporting NO_NEW_VALUABLE_FINDINGS for the same ledger/comment set.

ELIMINATE_SORT(RuleTypeClass.REWRITE),

PUSH_DOWN_AGG_THROUGH_JOIN_ONE_SIDE(RuleTypeClass.REWRITE),
PUSH_DOWN_AGG_WITH_DISTINCT_THROUGH_JOIN_ONE_SIDE(RuleTypeClass.REWRITE),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Removing this enum makes existing rule-control settings fail during parsing. disable_nereids_rules is a forwarded session variable, and SessionVariable#getDisableNereidsRules() still calls RuleType.valueOf(ruleName) for every configured name, so a deployment or query hint that currently has PUSH_DOWN_AGG_WITH_DISTINCT_THROUGH_JOIN_ONE_SIDE saved will now throw IllegalArgumentException instead of just disabling a now-removed implementation. Since this rule name was user-addressable through disable_nereids_rules/CBO rule hints, please keep the enum value as a no-op compatibility alias, or add explicit migration handling for the old name.

@morrySnow morrySnow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left one finding from the code review — using order-sensitive List.equals for multi-column distinct key comparison could miss optimization opportunities.

}
if (commonDistinctKeys == null) {
commonDistinctKeys = distinctKeys;
} else if (!commonDistinctKeys.equals(distinctKeys)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PLAUSIBLE: getCommonDistinctKeys uses List.equals() to compare distinct keys, which is order-sensitive. For multi-column distinct aggregates like COUNT(DISTINCT a, b) and SUM(DISTINCT b, a) in the same query, the two functions share the same distinct tuple {a, b} but the argument lists are in different orders. [a, b].equals([b, a]) returns false, so the method returns Optional.empty() and the optimization is skipped entirely.

Impact: No wrong results — the fallback is to leave the original plan unchanged. But the dedup-pushdown optimization is missed for this (admittedly unusual) edge case.

Suggested fix: Sort the distinct key list before comparison (e.g., by ExprId), or convert to a Set<SlotReference> for order-independent comparison.

Suggested change
} else if (!commonDistinctKeys.equals(distinctKeys)) {
} else if (distinctKeys.size() != commonDistinctKeys.size()
|| !new HashSet<>(commonDistinctKeys).containsAll(distinctKeys)) {

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29928 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 9aff7419994f934394ab160ffdf2cf248688dbd9, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17659	4208	4145	4145
q2	2016	312	198	198
q3	10360	1449	863	863
q4	4686	467	347	347
q5	7506	857	571	571
q6	177	169	137	137
q7	782	856	636	636
q8	9342	1577	1579	1577
q9	5670	4418	4421	4418
q10	6765	1797	1568	1568
q11	506	340	317	317
q12	715	559	440	440
q13	18102	3380	2746	2746
q14	268	262	238	238
q15	q16	796	782	718	718
q17	999	1041	1064	1041
q18	7060	5664	5510	5510
q19	1311	1285	1120	1120
q20	802	657	536	536
q21	5869	2684	2485	2485
q22	453	359	317	317
Total cold run time: 101844 ms
Total hot run time: 29928 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4475	4298	4349	4298
q2	288	315	217	217
q3	4617	5014	4441	4441
q4	2097	2164	1409	1409
q5	4441	4331	4321	4321
q6	242	174	130	130
q7	1740	2175	1688	1688
q8	2548	2174	2207	2174
q9	8073	8146	7899	7899
q10	4805	4745	4348	4348
q11	588	465	382	382
q12	796	759	539	539
q13	3365	3570	2938	2938
q14	302	301	276	276
q15	q16	721	741	636	636
q17	1364	1407	1391	1391
q18	7931	7201	7241	7201
q19	1124	1123	1078	1078
q20	2199	2211	1943	1943
q21	5333	4626	4482	4482
q22	525	461	417	417
Total cold run time: 57574 ms
Total hot run time: 52208 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 174976 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 9aff7419994f934394ab160ffdf2cf248688dbd9, data reload: false

query5	4330	653	522	522
query6	451	224	207	207
query7	4928	594	351	351
query8	337	187	177	177
query9	8769	4116	4119	4116
query10	476	346	329	329
query11	5961	2371	2161	2161
query12	177	106	103	103
query13	1275	629	428	428
query14	6290	5344	4996	4996
query14_1	4355	4340	4319	4319
query15	218	211	184	184
query16	1021	496	489	489
query17	962	745	606	606
query18	2453	488	359	359
query19	212	202	165	165
query20	117	109	108	108
query21	240	160	135	135
query22	13674	13598	13428	13428
query23	17435	16622	16189	16189
query23_1	16220	16289	16260	16260
query24	7547	1813	1298	1298
query24_1	1326	1318	1305	1305
query25	590	494	400	400
query26	1354	359	221	221
query27	2621	594	385	385
query28	4439	2047	2069	2047
query29	1122	660	522	522
query30	351	267	228	228
query31	1136	1093	1000	1000
query32	102	65	62	62
query33	572	330	257	257
query34	1150	1147	651	651
query35	758	782	680	680
query36	1401	1378	1219	1219
query37	152	108	93	93
query38	1890	1693	1664	1664
query39	918	928	893	893
query39_1	887	877	894	877
query40	252	164	146	146
query41	66	67	63	63
query42	100	95	96	95
query43	327	331	286	286
query44	1469	829	789	789
query45	203	189	183	183
query46	1099	1239	753	753
query47	2367	2352	2250	2250
query48	429	434	312	312
query49	595	432	330	330
query50	1107	455	338	338
query51	4436	4326	4320	4320
query52	87	88	75	75
query53	269	284	203	203
query54	275	229	227	227
query55	78	72	67	67
query56	303	292	276	276
query57	1448	1397	1332	1332
query58	292	267	259	259
query59	1588	1674	1437	1437
query60	303	282	256	256
query61	151	149	153	149
query62	698	654	576	576
query63	244	213	212	212
query64	2565	751	604	604
query65	4862	4771	4810	4771
query66	1833	535	397	397
query67	29666	29570	29408	29408
query68	3218	1561	997	997
query69	421	307	286	286
query70	1049	966	969	966
query71	345	326	324	324
query72	2896	2600	2438	2438
query73	848	803	449	449
query74	5116	4952	4746	4746
query75	2614	2594	2209	2209
query76	2336	1247	799	799
query77	348	383	291	291
query78	12434	12513	11825	11825
query79	1422	1099	801	801
query80	727	565	471	471
query81	468	323	283	283
query82	568	154	123	123
query83	391	325	299	299
query84	321	171	137	137
query85	974	608	535	535
query86	400	285	281	281
query87	1841	1824	1781	1781
query88	3831	2843	2831	2831
query89	459	409	355	355
query90	1816	204	208	204
query91	207	199	173	173
query92	66	64	58	58
query93	1599	1577	1070	1070
query94	622	338	312	312
query95	785	493	475	475
query96	1120	766	366	366
query97	2686	2729	2548	2548
query98	221	208	229	208
query99	1198	1146	1030	1030
Total cold run time: 259076 ms
Total hot run time: 174976 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 25.34 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 9aff7419994f934394ab160ffdf2cf248688dbd9, data reload: false

query1	0.01	0.01	0.01
query2	0.10	0.05	0.05
query3	0.26	0.14	0.14
query4	1.61	0.15	0.14
query5	0.23	0.23	0.22
query6	1.27	1.08	1.05
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.39	0.31	0.32
query10	0.55	0.54	0.55
query11	0.20	0.15	0.14
query12	0.19	0.15	0.15
query13	0.48	0.47	0.47
query14	1.03	1.02	1.01
query15	0.63	0.60	0.59
query16	0.32	0.33	0.34
query17	1.13	1.11	1.09
query18	0.23	0.21	0.22
query19	2.08	1.93	1.97
query20	0.02	0.01	0.01
query21	15.46	0.22	0.13
query22	4.93	0.05	0.05
query23	16.14	0.32	0.13
query24	2.99	0.42	0.32
query25	0.12	0.05	0.05
query26	0.73	0.22	0.16
query27	0.04	0.04	0.03
query28	3.47	0.94	0.51
query29	12.54	4.32	3.44
query30	0.28	0.16	0.16
query31	2.78	0.60	0.31
query32	3.23	0.59	0.50
query33	3.16	3.20	3.27
query34	15.40	4.21	3.53
query35	3.50	3.58	3.57
query36	0.54	0.42	0.43
query37	0.09	0.08	0.07
query38	0.06	0.04	0.04
query39	0.04	0.03	0.04
query40	0.18	0.16	0.16
query41	0.09	0.04	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.04
Total cold run time: 96.68 s
Total hot run time: 25.34 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 85.45% (47/55) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 39.17% (47/120) 🎉
Increment coverage report
Complete coverage report

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