You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by "englefly (via GitHub)" <gi...@apache.org> on 2026/02/14 02:40:55 UTC
[PR] [feat](eager-agg)Eager agg 0214 [doris]
englefly opened a new pull request, #60757:
URL: https://github.com/apache/doris/pull/60757
### What problem does this PR solve?
Issue Number: close #xxx
Related PR: #xxx
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: https://github.com/apache/doris-website/pull/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 -->
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "doris-robot (via GitHub)" <gi...@apache.org>.
doris-robot commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3983060686
<details>
<summary>TPC-H: <b>Total hot run time: 27530 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 4fd41b6bde3a7c72efcd889b0042d38412de1f9b, data reload: false
------ Round 1 ----------------------------------
============================================
q1 17645 4563 4312 4312
q2 q3 10643 807 532 532
q4 4711 357 255 255
q5 7926 1222 1047 1047
q6 215 174 145 145
q7 834 850 660 660
q8 10430 1479 1357 1357
q9 6384 4772 4680 4680
q10 6484 1937 1652 1652
q11 488 252 236 236
q12 764 576 469 469
q13 18133 2941 2182 2182
q14 244 234 208 208
q15 957 785 807 785
q16 804 735 703 703
q17 763 915 403 403
q18 6014 5353 5262 5262
q19 1232 980 626 626
q20 512 492 391 391
q21 4475 1860 1375 1375
q22 348 287 250 250
Total cold run time: 100006 ms
Total hot run time: 27530 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1 4497 4324 4347 4324
q2 q3 3855 4301 3770 3770
q4 848 1179 755 755
q5 4022 4365 4341 4341
q6 175 166 140 140
q7 1716 1611 1502 1502
q8 2416 2650 2691 2650
q9 7767 7386 7470 7386
q10 3712 3950 3571 3571
q11 524 434 411 411
q12 506 615 449 449
q13 2750 3366 2380 2380
q14 286 319 284 284
q15 897 840 788 788
q16 728 764 729 729
q17 1204 1397 1339 1339
q18 7101 6735 6660 6660
q19 953 909 956 909
q20 2106 2185 2029 2029
q21 4224 3928 3547 3547
q22 507 434 378 378
Total cold run time: 50794 ms
Total hot run time: 48342 ms
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3942436586
# FE UT Coverage Report
Increment line coverage ` 63.48% (412/649)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/60757_742e5dc1a961547a7c51c084b61e93ee833a0d02/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/60757_742e5dc1a961547a7c51c084b61e93ee833a0d02/fe_report/index.html)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3393372966
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
+ if (aggFunction.arity() > 0 && aggFunction.child(0) instanceof If
+ && !(aggFunction instanceof Count)) {
+ // Decompose Sum/Max/Min(If(cond, a, b)) into separate agg functions.
+ // Count(If(...)) is NOT decomposed here because the top-level
+ // replacement (Count->Sum rollup) cannot match the decomposed
+ // Count(a)/Count(b) as sub-expressions of the original Count(If(cond,a,b)).
+ // Count(If(...)) is pushed down as-is and rolled up normally.
+ If body = (If) (aggFunction).child(0);
+ Set<Slot> valueSlots = Sets.newHashSet(body.getTrueValue().getInputSlots());
+ valueSlots.addAll(body.getFalseValue().getInputSlots());
+ if (body.getCondition().getInputSlots().stream().anyMatch(s -> valueSlots.contains(s))) {
+ // do not push down sum(if a then a else b)
+ return agg;
+ }
Review Comment:
这个注释 的意思是如果 condition中的slot和value中的slot有交集就不下推
sum(if(x, x, y)) 为例,如果下推,就会产生 sum(x) group by x 的聚合,这个聚合没有意义
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "doris-robot (via GitHub)" <gi...@apache.org>.
doris-robot commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3948404643
<details>
<summary>TPC-DS: <b>Total hot run time: 152941 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit d8703bf984891db2830e567b16976638d0b6c9b4, data reload: false
query5 4332 635 506 506
query6 314 233 219 219
query7 4213 474 270 270
query8 348 261 234 234
query9 8726 2754 2747 2747
query10 518 385 341 341
query11 7431 5864 5548 5548
query12 178 125 127 125
query13 1265 455 355 355
query14 6194 3794 3571 3571
query14_1 2862 2837 2877 2837
query15 204 194 175 175
query16 992 471 427 427
query17 1113 726 623 623
query18 2476 457 352 352
query19 219 209 185 185
query20 137 129 130 129
query21 224 178 120 120
query22 4791 5091 4882 4882
query23 16713 16010 15670 15670
query23_1 15828 15890 15867 15867
query24 7874 1707 1306 1306
query24_1 1290 1287 1308 1287
query25 580 473 416 416
query26 1237 255 142 142
query27 2780 471 285 285
query28 4500 1867 1858 1858
query29 793 566 477 477
query30 309 237 208 208
query31 1340 1299 1225 1225
query32 77 68 74 68
query33 533 353 271 271
query34 915 914 548 548
query35 644 665 611 611
query36 1062 1130 946 946
query37 137 92 83 83
query38 2994 2898 2904 2898
query39 894 905 853 853
query39_1 844 822 813 813
query40 227 153 134 134
query41 61 59 57 57
query42 300 297 301 297
query43 237 252 227 227
query44
query45 192 188 180 180
query46 876 970 600 600
query47 2095 2129 2033 2033
query48 312 314 231 231
query49 665 456 379 379
query50 664 279 218 218
query51 4093 4044 4111 4044
query52 286 296 278 278
query53 291 341 280 280
query54 286 262 253 253
query55 87 84 80 80
query56 310 306 311 306
query57 1348 1336 1274 1274
query58 286 273 266 266
query59 1330 1487 1316 1316
query60 344 343 315 315
query61 177 144 148 144
query62 620 584 531 531
query63 304 279 268 268
query64 4979 1279 983 983
query65
query66 1450 471 358 358
query67 16381 16336 16291 16291
query68
query69 382 311 284 284
query70 983 927 967 927
query71 333 300 290 290
query72 2829 2664 2502 2502
query73 535 545 331 331
query74 9976 9926 9829 9829
query75 2842 2762 2475 2475
query76 2325 1036 686 686
query77 373 405 318 318
query78 11239 11401 10714 10714
query79 1129 805 592 592
query80 717 649 562 562
query81 516 283 242 242
query82 1339 149 119 119
query83 379 282 253 253
query84 259 127 102 102
query85 933 572 522 522
query86 376 309 286 286
query87 3133 3076 2978 2978
query88 3515 2655 2634 2634
query89 423 374 342 342
query90 1933 195 170 170
query91 170 153 129 129
query92 83 73 69 69
query93 899 821 494 494
query94 453 319 292 292
query95 584 342 330 330
query96 636 510 226 226
query97 2490 2501 2424 2424
query98 232 222 213 213
query99 1019 996 865 865
Total cold run time: 232655 ms
Total hot run time: 152941 ms
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "doris-robot (via GitHub)" <gi...@apache.org>.
doris-robot commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3900698464
<details>
<summary>TPC-DS: <b>Total hot run time: 152972 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit f90def55f7e6340e69de14ccf648f940ba0378c3, data reload: false
query5 4329 637 531 531
query6 317 225 203 203
query7 4227 478 270 270
query8 343 241 221 221
query9 8748 2771 2768 2768
query10 533 378 377 377
query11 7388 5866 5543 5543
query12 183 127 126 126
query13 1294 461 354 354
query14 5720 3818 3527 3527
query14_1 2773 2771 2820 2771
query15 206 197 179 179
query16 1025 481 471 471
query17 1110 729 624 624
query18 2443 442 358 358
query19 238 211 183 183
query20 136 134 131 131
query21 228 151 126 126
query22 4743 5049 4848 4848
query23 16875 15925 15889 15889
query23_1 15876 15848 15839 15839
query24 7710 1689 1329 1329
query24_1 1359 1302 1260 1260
query25 585 526 468 468
query26 1018 269 165 165
query27 2721 466 288 288
query28 4499 1890 1862 1862
query29 864 554 465 465
query30 321 247 215 215
query31 1360 1300 1208 1208
query32 82 74 69 69
query33 528 340 275 275
query34 924 897 576 576
query35 648 682 593 593
query36 1067 1146 917 917
query37 136 90 79 79
query38 2965 2871 2868 2868
query39 893 863 852 852
query39_1 829 828 836 828
query40 228 148 133 133
query41 63 59 58 58
query42 304 301 290 290
query43 240 246 220 220
query44
query45 195 194 177 177
query46 873 969 609 609
query47 2112 2166 2077 2077
query48 330 312 232 232
query49 632 461 378 378
query50 675 272 220 220
query51 4106 4078 4062 4062
query52 286 302 282 282
query53 290 334 294 294
query54 310 272 255 255
query55 136 90 82 82
query56 310 310 302 302
query57 1375 1345 1284 1284
query58 295 294 270 270
query59 1378 1436 1255 1255
query60 344 341 336 336
query61 148 145 146 145
query62 642 582 534 534
query63 302 277 280 277
query64 4742 1276 1015 1015
query65
query66 1410 456 358 358
query67 16535 16302 16314 16302
query68
query69 382 323 296 296
query70 939 980 987 980
query71 346 311 289 289
query72 2742 2649 2332 2332
query73 542 539 318 318
query74 10079 9967 9769 9769
query75 2844 2753 2433 2433
query76 2301 1042 667 667
query77 357 366 294 294
query78 11301 11406 10646 10646
query79 2570 813 600 600
query80 1726 601 534 534
query81 571 284 244 244
query82 1009 150 118 118
query83 330 262 253 253
query84 250 117 102 102
query85 917 471 425 425
query86 412 297 295 295
query87 3135 3093 2963 2963
query88 3539 2685 2670 2670
query89 417 362 337 337
query90 2023 182 165 165
query91 166 155 132 132
query92 78 74 74 74
query93 1132 813 516 516
query94 633 334 287 287
query95 599 391 303 303
query96 641 511 228 228
query97 2449 2476 2435 2435
query98 238 214 218 214
query99 996 986 926 926
Total cold run time: 235608 ms
Total hot run time: 152972 ms
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3392779919
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
Review Comment:
可以方便定位,通过日志可以确定是pushDownAggregation 之前的rule 有问题
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3982522327
run buildall
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly merged PR #60757:
URL: https://github.com/apache/doris/pull/60757
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3393791410
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java:
##########
@@ -0,0 +1,625 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.StatsDerive;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.statistics.ColumnStatistic;
+import org.apache.doris.statistics.Statistics;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * eager aggregation
+ * agg[sum(t1.A) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->T1(A, B, C)
+ * ->T2(D)
+ *
+ * =>
+ * agg[sum(x) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->agg[sum(A) as x, group by B]
+ * ->T1(A, B, C)
+ * ->T2(D)
+ */
+public class EagerAggRewriter extends DefaultPlanRewriter<PushDownAggContext> {
+ private static final double LOWER_AGGREGATE_EFFECT_COEFFICIENT = 10000;
+ private static final double LOW_AGGREGATE_EFFECT_COEFFICIENT = 1000;
+ private static final double MEDIUM_AGGREGATE_EFFECT_COEFFICIENT = 100;
+ private final StatsDerive derive = new StatsDerive(false);
+
+ @Override
+ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, PushDownAggContext context) {
+ boolean toLeft = false;
+ boolean toRight = false;
+ boolean pushHere = false;
+ if (context.getAggFunctions().isEmpty()) {
+ // select t1.v from t1 join t2 on t1.id = t2.id group by t1.v, t2.v
+ // if no agg function, try to push agg to the child which contains all group keys
+ // TODO: consider t1.rows/(t1.id, t1.v).ndv and t2.rows/(t2.id, t2.v).ndv to determine push target
+ if (join.left().getOutputSet().containsAll(context.getGroupKeys())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(context.getGroupKeys())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ } else {
+ for (AggregateFunction aggFunc : context.getAggFunctions()) {
+ if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ }
+ }
+
+ if (pushHere || (toLeft && toRight)) {
Review Comment:
这里永远是false,应该去掉。
这是因为上面的判断逻辑改了,这里没有跟着改。最开始考虑一部分推左边,一部分推右边的场景,所以会出现toLeft = toRight = true.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3392779919
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
Review Comment:
可以方便定位,通过日志可以确定是pushDownAggregation 之前的rule 有问题
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
Review Comment:
可以方便定位,通过日志可以确定是pushDownAggregation 之前的rule 有问题
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3901294575
# FE Regression Coverage Report
Increment line coverage ` 74.58% (484/649)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/60757_f90def55f7e6340e69de14ccf648f940ba0378c3_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/60757_f90def55f7e6340e69de14ccf648f940ba0378c3_merge_fe/report/index.html)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3393791410
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java:
##########
@@ -0,0 +1,625 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.StatsDerive;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.statistics.ColumnStatistic;
+import org.apache.doris.statistics.Statistics;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * eager aggregation
+ * agg[sum(t1.A) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->T1(A, B, C)
+ * ->T2(D)
+ *
+ * =>
+ * agg[sum(x) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->agg[sum(A) as x, group by B]
+ * ->T1(A, B, C)
+ * ->T2(D)
+ */
+public class EagerAggRewriter extends DefaultPlanRewriter<PushDownAggContext> {
+ private static final double LOWER_AGGREGATE_EFFECT_COEFFICIENT = 10000;
+ private static final double LOW_AGGREGATE_EFFECT_COEFFICIENT = 1000;
+ private static final double MEDIUM_AGGREGATE_EFFECT_COEFFICIENT = 100;
+ private final StatsDerive derive = new StatsDerive(false);
+
+ @Override
+ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, PushDownAggContext context) {
+ boolean toLeft = false;
+ boolean toRight = false;
+ boolean pushHere = false;
+ if (context.getAggFunctions().isEmpty()) {
+ // select t1.v from t1 join t2 on t1.id = t2.id group by t1.v, t2.v
+ // if no agg function, try to push agg to the child which contains all group keys
+ // TODO: consider t1.rows/(t1.id, t1.v).ndv and t2.rows/(t2.id, t2.v).ndv to determine push target
+ if (join.left().getOutputSet().containsAll(context.getGroupKeys())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(context.getGroupKeys())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ } else {
+ for (AggregateFunction aggFunc : context.getAggFunctions()) {
+ if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ }
+ }
+
+ if (pushHere || (toLeft && toRight)) {
Review Comment:
toLeft 和 toRight 的设置
```
for (AggregateFunction aggFunc : context.getAggFunctions()) {
if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
toLeft = true;
} else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
toRight = true;
} else {
pushHere = true;
}
}
```
存在这种场景:context.getAggFunctions()= { sum(left_col), sum(right_col)}
于是 toLeft 和 toRight 都是true
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-4002470488
PR approved by at least one committer and no changes requested.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-4002470725
PR approved by anyone and no changes requested.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3948856126
# FE Regression Coverage Report
Increment line coverage ` 74.58% (484/649)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/60757_d8703bf984891db2830e567b16976638d0b6c9b4_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/60757_d8703bf984891db2830e567b16976638d0b6c9b4_merge_fe/report/index.html)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "doris-robot (via GitHub)" <gi...@apache.org>.
doris-robot commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3942248930
<details>
<summary>TPC-H: <b>Total hot run time: 27615 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 742e5dc1a961547a7c51c084b61e93ee833a0d02, data reload: false
------ Round 1 ----------------------------------
============================================
q1 17674 4467 4292 4292
q2 q3 10663 795 510 510
q4 4678 354 255 255
q5 7565 1188 1027 1027
q6 176 181 142 142
q7 787 845 662 662
q8 9302 1443 1298 1298
q9 4760 4714 4661 4661
q10 6298 1902 1670 1670
q11 490 267 241 241
q12 715 563 475 475
q13 18062 2902 2149 2149
q14 229 231 213 213
q15 934 793 801 793
q16 743 724 674 674
q17 700 844 415 415
q18 5919 5499 5309 5309
q19 1203 989 596 596
q20 512 488 380 380
q21 4436 1935 1581 1581
q22 352 336 272 272
Total cold run time: 96198 ms
Total hot run time: 27615 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1 4639 4529 4581 4529
q2 q3 4008 4364 3824 3824
q4 862 1164 744 744
q5 4024 4387 4448 4387
q6 184 172 139 139
q7 1798 1604 1481 1481
q8 2443 2711 2539 2539
q9 7574 7446 7268 7268
q10 3760 3926 3589 3589
q11 530 456 429 429
q12 520 604 485 485
q13 2957 3205 2335 2335
q14 280 302 271 271
q15 882 812 795 795
q16 690 757 758 757
q17 1132 1440 1413 1413
q18 7288 6837 6699 6699
q19 860 877 953 877
q20 2053 2124 2029 2029
q21 3949 3482 3264 3264
q22 453 437 404 404
Total cold run time: 50886 ms
Total hot run time: 48258 ms
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3942765168
# FE Regression Coverage Report
Increment line coverage ` 74.58% (484/649)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/60757_742e5dc1a961547a7c51c084b61e93ee833a0d02_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/60757_742e5dc1a961547a7c51c084b61e93ee833a0d02_merge_fe/report/index.html)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3948255941
run buildall
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3393679623
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java:
##########
@@ -0,0 +1,625 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.StatsDerive;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.statistics.ColumnStatistic;
+import org.apache.doris.statistics.Statistics;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * eager aggregation
+ * agg[sum(t1.A) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->T1(A, B, C)
+ * ->T2(D)
+ *
+ * =>
+ * agg[sum(x) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->agg[sum(A) as x, group by B]
+ * ->T1(A, B, C)
+ * ->T2(D)
+ */
+public class EagerAggRewriter extends DefaultPlanRewriter<PushDownAggContext> {
+ private static final double LOWER_AGGREGATE_EFFECT_COEFFICIENT = 10000;
+ private static final double LOW_AGGREGATE_EFFECT_COEFFICIENT = 1000;
+ private static final double MEDIUM_AGGREGATE_EFFECT_COEFFICIENT = 100;
+ private final StatsDerive derive = new StatsDerive(false);
+
+ @Override
+ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, PushDownAggContext context) {
+ boolean toLeft = false;
+ boolean toRight = false;
+ boolean pushHere = false;
+ if (context.getAggFunctions().isEmpty()) {
+ // select t1.v from t1 join t2 on t1.id = t2.id group by t1.v, t2.v
+ // if no agg function, try to push agg to the child which contains all group keys
+ // TODO: consider t1.rows/(t1.id, t1.v).ndv and t2.rows/(t2.id, t2.v).ndv to determine push target
+ if (join.left().getOutputSet().containsAll(context.getGroupKeys())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(context.getGroupKeys())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ } else {
+ for (AggregateFunction aggFunc : context.getAggFunctions()) {
+ if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
Review Comment:
如果没有agg function,就按group key 来决定下推,这相当于agg function就是求 group key的distinct。
这个分支有agg function,就只需要下推agg function,下推后的group key 是join key
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3392837049
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
Review Comment:
后面补充这些函数的支持
IF(cond, a, b) | 条件为真返回 a,否则返回 b
IFNULL(a, b) | a 不为 NULL 返回 a,否则返回 b(别名:NVL)
CASE WHEN | 多分支条件逻辑,类似 switch-case
COALESCE(a, b, ...) | 返回参数列表中第一个非 NULL 的值
NULLIF(a, b) | 若 a = b 返回 NULL,否则返回 a
LEAST(a, b, ...) | 返回多个参数中的最小值,任一为 NULL 则返回 NULL
GREATEST(a, b, ...) | 返回多个参数中的最大值,任一为 NULL 则返回 NULL
NULL_OR_EMPTY(str) | 字符串为 NULL 或空串时返回 true
NOT_NULL_OR_EMPTY(str) | 字符串既不为 NULL 也不为空串时返回 true
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "doris-robot (via GitHub)" <gi...@apache.org>.
doris-robot commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3900663739
<details>
<summary>TPC-H: <b>Total hot run time: 27608 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit f90def55f7e6340e69de14ccf648f940ba0378c3, data reload: false
------ Round 1 ----------------------------------
============================================
q1 17676 4511 4329 4329
q2 q3 10643 785 510 510
q4 4682 359 249 249
q5 7557 1242 1014 1014
q6 177 175 146 146
q7 780 840 669 669
q8 9752 1500 1332 1332
q9 5280 4783 4753 4753
q10 6324 1922 1668 1668
q11 471 268 232 232
q12 815 576 468 468
q13 18066 2941 2172 2172
q14 235 230 217 217
q15 954 802 795 795
q16 791 727 674 674
q17 711 861 413 413
q18 6009 5490 5268 5268
q19 1508 998 637 637
q20 491 485 401 401
q21 4475 1837 1419 1419
q22 337 296 242 242
Total cold run time: 97734 ms
Total hot run time: 27608 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1 4558 4341 4551 4341
q2 q3 3866 4268 3753 3753
q4 850 1152 768 768
q5 4008 4331 4335 4331
q6 182 173 147 147
q7 1721 1619 1491 1491
q8 2440 2655 2528 2528
q9 7988 7518 7376 7376
q10 3764 3989 3571 3571
q11 519 442 410 410
q12 478 612 491 491
q13 2740 3334 2324 2324
q14 289 333 290 290
q15 906 803 793 793
q16 742 791 703 703
q17 1169 1438 1330 1330
q18 7195 6885 6866 6866
q19 886 918 945 918
q20 2057 2155 2131 2131
q21 3967 3516 3302 3302
q22 462 459 416 416
Total cold run time: 50787 ms
Total hot run time: 48280 ms
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3900701928
# FE UT Coverage Report
Increment line coverage ` 63.48% (412/649)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/60757_f90def55f7e6340e69de14ccf648f940ba0378c3/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/60757_f90def55f7e6340e69de14ccf648f940ba0378c3/fe_report/index.html)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3942179475
run buildall
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "Thearas (via GitHub)" <gi...@apache.org>.
Thearas commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3900510399
Thank you for your contribution to Apache Doris.
Don't know what should be done next? See [How to process your PR](https://cwiki.apache.org/confluence/display/DORIS/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?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3900510863
run buildall
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3393372966
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
+ if (aggFunction.arity() > 0 && aggFunction.child(0) instanceof If
+ && !(aggFunction instanceof Count)) {
+ // Decompose Sum/Max/Min(If(cond, a, b)) into separate agg functions.
+ // Count(If(...)) is NOT decomposed here because the top-level
+ // replacement (Count->Sum rollup) cannot match the decomposed
+ // Count(a)/Count(b) as sub-expressions of the original Count(If(cond,a,b)).
+ // Count(If(...)) is pushed down as-is and rolled up normally.
+ If body = (If) (aggFunction).child(0);
+ Set<Slot> valueSlots = Sets.newHashSet(body.getTrueValue().getInputSlots());
+ valueSlots.addAll(body.getFalseValue().getInputSlots());
+ if (body.getCondition().getInputSlots().stream().anyMatch(s -> valueSlots.contains(s))) {
+ // do not push down sum(if a then a else b)
+ return agg;
+ }
Review Comment:
这个注释 的意思是如果 condition中的slot和value中的slot有交集就不下推
sum(if(x, x, y)) 为例,如果下推,就会产生 sum(x) group by x 的聚合,这个聚合没有意义
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3393791410
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java:
##########
@@ -0,0 +1,625 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.StatsDerive;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.statistics.ColumnStatistic;
+import org.apache.doris.statistics.Statistics;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * eager aggregation
+ * agg[sum(t1.A) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->T1(A, B, C)
+ * ->T2(D)
+ *
+ * =>
+ * agg[sum(x) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->agg[sum(A) as x, group by B]
+ * ->T1(A, B, C)
+ * ->T2(D)
+ */
+public class EagerAggRewriter extends DefaultPlanRewriter<PushDownAggContext> {
+ private static final double LOWER_AGGREGATE_EFFECT_COEFFICIENT = 10000;
+ private static final double LOW_AGGREGATE_EFFECT_COEFFICIENT = 1000;
+ private static final double MEDIUM_AGGREGATE_EFFECT_COEFFICIENT = 100;
+ private final StatsDerive derive = new StatsDerive(false);
+
+ @Override
+ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, PushDownAggContext context) {
+ boolean toLeft = false;
+ boolean toRight = false;
+ boolean pushHere = false;
+ if (context.getAggFunctions().isEmpty()) {
+ // select t1.v from t1 join t2 on t1.id = t2.id group by t1.v, t2.v
+ // if no agg function, try to push agg to the child which contains all group keys
+ // TODO: consider t1.rows/(t1.id, t1.v).ndv and t2.rows/(t2.id, t2.v).ndv to determine push target
+ if (join.left().getOutputSet().containsAll(context.getGroupKeys())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(context.getGroupKeys())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ } else {
+ for (AggregateFunction aggFunc : context.getAggFunctions()) {
+ if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ }
+ }
+
+ if (pushHere || (toLeft && toRight)) {
Review Comment:
toLeft 和 toRight 的设置
```
for (AggregateFunction aggFunc : context.getAggFunctions()) {
if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
toLeft = true;
} else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
toRight = true;
} else {
pushHere = true;
}
}
```
存在这种场景:context.getAggFunctions()= { sum(left_col), sum(right_col)}
于是 toLeft 和 toRight 都是true
这里有冗余的比较。如果toLeft是true,再遇到toRight时就可以停止了。
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "doris-robot (via GitHub)" <gi...@apache.org>.
doris-robot commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3942286702
<details>
<summary>TPC-DS: <b>Total hot run time: 152691 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 742e5dc1a961547a7c51c084b61e93ee833a0d02, data reload: false
query5 4334 629 523 523
query6 326 217 218 217
query7 4218 477 265 265
query8 321 236 223 223
query9 8703 2739 2727 2727
query10 518 373 334 334
query11 7390 5856 5638 5638
query12 175 122 125 122
query13 1258 465 327 327
query14 5662 3807 3591 3591
query14_1 2831 2752 2762 2752
query15 195 191 172 172
query16 978 475 435 435
query17 1074 689 593 593
query18 2434 447 337 337
query19 205 207 172 172
query20 133 122 123 122
query21 218 145 119 119
query22 4773 4861 4722 4722
query23 16676 15900 15654 15654
query23_1 15678 15921 15741 15741
query24 7404 1694 1293 1293
query24_1 1281 1247 1268 1247
query25 560 488 433 433
query26 1299 272 161 161
query27 2918 479 295 295
query28 4799 1904 1983 1904
query29 870 574 463 463
query30 310 243 207 207
query31 1355 1276 1216 1216
query32 78 72 72 72
query33 502 324 277 277
query34 903 925 572 572
query35 635 671 635 635
query36 1076 1144 942 942
query37 133 97 80 80
query38 2896 2940 2844 2844
query39 880 872 859 859
query39_1 806 847 852 847
query40 234 153 131 131
query41 61 60 61 60
query42 300 298 297 297
query43 249 240 221 221
query44
query45 199 192 179 179
query46 886 988 604 604
query47 2105 2143 2052 2052
query48 328 323 240 240
query49 646 470 398 398
query50 707 287 220 220
query51 4089 4066 4035 4035
query52 286 291 281 281
query53 294 351 284 284
query54 303 284 283 283
query55 100 85 92 85
query56 324 342 317 317
query57 1342 1322 1269 1269
query58 299 289 280 280
query59 1332 1415 1283 1283
query60 355 340 337 337
query61 166 182 170 170
query62 635 596 543 543
query63 311 276 278 276
query64 5080 1342 1108 1108
query65
query66 1476 445 368 368
query67 16372 16570 16285 16285
query68
query69 392 300 279 279
query70 893 1016 944 944
query71 351 318 304 304
query72 2790 2677 2388 2388
query73 542 549 318 318
query74 10022 9950 9781 9781
query75 2845 2783 2456 2456
query76 2299 1038 698 698
query77 355 393 311 311
query78 11221 11293 10643 10643
query79 3089 816 589 589
query80 1718 621 522 522
query81 596 278 252 252
query82 1016 147 118 118
query83 336 266 245 245
query84 251 130 98 98
query85 928 490 440 440
query86 448 299 292 292
query87 3141 3062 2943 2943
query88 3522 2669 2649 2649
query89 433 362 344 344
query90 2002 178 177 177
query91 171 158 133 133
query92 75 72 68 68
query93 1359 815 494 494
query94 637 330 286 286
query95 584 391 309 309
query96 628 511 226 226
query97 2461 2513 2411 2411
query98 237 218 217 217
query99 989 998 920 920
Total cold run time: 236738 ms
Total hot run time: 152691 ms
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "doris-robot (via GitHub)" <gi...@apache.org>.
doris-robot commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3983106266
<details>
<summary>TPC-DS: <b>Total hot run time: 153438 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 4fd41b6bde3a7c72efcd889b0042d38412de1f9b, data reload: false
query5 4330 637 518 518
query6 337 227 214 214
query7 4211 467 263 263
query8 327 240 234 234
query9 8700 2744 2734 2734
query10 541 405 344 344
query11 7324 5836 5674 5674
query12 197 131 126 126
query13 1280 479 364 364
query14 6196 3868 3591 3591
query14_1 2815 2807 2839 2807
query15 205 197 178 178
query16 994 493 469 469
query17 1122 727 629 629
query18 2580 447 354 354
query19 221 217 182 182
query20 136 138 134 134
query21 227 151 125 125
query22 4716 4869 4706 4706
query23 16890 15932 15717 15717
query23_1 15765 15975 15999 15975
query24 7335 1720 1252 1252
query24_1 1255 1268 1282 1268
query25 582 487 423 423
query26 1275 273 161 161
query27 3040 482 292 292
query28 4503 1906 1911 1906
query29 811 578 484 484
query30 310 246 213 213
query31 1367 1289 1203 1203
query32 81 73 71 71
query33 510 337 271 271
query34 911 919 564 564
query35 670 679 606 606
query36 1113 1064 976 976
query37 130 96 84 84
query38 2967 2986 2823 2823
query39 874 865 843 843
query39_1 833 832 826 826
query40 233 153 138 138
query41 64 60 60 60
query42 313 310 299 299
query43 247 251 222 222
query44
query45 201 188 181 181
query46 873 993 606 606
query47 2120 2126 2055 2055
query48 310 321 227 227
query49 619 462 393 393
query50 678 286 217 217
query51 4280 4185 4098 4098
query52 287 291 281 281
query53 290 333 291 291
query54 303 277 265 265
query55 94 86 88 86
query56 318 332 299 299
query57 1370 1353 1270 1270
query58 292 277 305 277
query59 1339 1463 1316 1316
query60 341 338 325 325
query61 165 139 146 139
query62 649 586 553 553
query63 318 278 273 273
query64 4973 1268 990 990
query65
query66 1425 460 353 353
query67 16383 16424 16299 16299
query68
query69 381 303 281 281
query70 986 957 937 937
query71 347 313 303 303
query72 2859 2720 2437 2437
query73 542 558 345 345
query74 10009 9911 9810 9810
query75 2844 2755 2485 2485
query76 2297 1037 701 701
query77 370 380 324 324
query78 11304 11348 10691 10691
query79 1165 829 600 600
query80 1359 657 584 584
query81 575 284 259 259
query82 1277 158 119 119
query83 378 275 247 247
query84 253 118 102 102
query85 1093 553 433 433
query86 438 325 297 297
query87 3157 3132 2992 2992
query88 3568 2669 2647 2647
query89 435 373 345 345
query90 1877 177 168 168
query91 163 162 133 133
query92 82 77 71 71
query93 911 820 507 507
query94 652 332 297 297
query95 600 342 322 322
query96 641 519 224 224
query97 2469 2514 2481 2481
query98 230 220 223 220
query99 1010 1002 927 927
Total cold run time: 234638 ms
Total hot run time: 153438 ms
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3970254500
# FE UT Coverage Report
Increment line coverage ` 63.48% (412/649)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/60757_295bd29e62c92509a9b96dd18e2ee72013855af9/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/60757_295bd29e62c92509a9b96dd18e2ee72013855af9/fe_report/index.html)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-4002441738
@copilot 帮我写一下这个pr的描述
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)Eager agg 0214 [doris]
Posted by "doris-robot (via GitHub)" <gi...@apache.org>.
doris-robot commented on PR #60757:
URL: https://github.com/apache/doris/pull/60757#issuecomment-3948370160
<details>
<summary>TPC-H: <b>Total hot run time: 27628 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit d8703bf984891db2830e567b16976638d0b6c9b4, data reload: false
------ Round 1 ----------------------------------
============================================
q1 17629 4560 4300 4300
q2 q3 10656 766 520 520
q4 4671 343 250 250
q5 7556 1201 1018 1018
q6 184 179 158 158
q7 783 847 679 679
q8 9311 1455 1360 1360
q9 4820 4790 4693 4693
q10 6303 1893 1632 1632
q11 469 265 253 253
q12 716 575 475 475
q13 18061 2914 2209 2209
q14 231 231 214 214
q15 958 789 790 789
q16 781 713 679 679
q17 705 876 433 433
q18 5896 5413 5225 5225
q19 1387 977 628 628
q20 503 512 392 392
q21 4490 1935 1461 1461
q22 344 291 260 260
Total cold run time: 96454 ms
Total hot run time: 27628 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1 4449 4355 4381 4355
q2 q3 3879 4388 3818 3818
q4 880 1201 804 804
q5 4078 4385 4305 4305
q6 191 177 145 145
q7 1743 1629 1506 1506
q8 2459 2686 2593 2593
q9 7828 7368 7310 7310
q10 3779 3983 3684 3684
q11 510 432 455 432
q12 543 627 466 466
q13 2779 3399 2405 2405
q14 295 303 279 279
q15 845 825 838 825
q16 740 779 713 713
q17 1162 1456 1380 1380
q18 7085 6796 6669 6669
q19 886 879 920 879
q20 2143 2232 2014 2014
q21 4168 3587 3411 3411
q22 458 445 418 418
Total cold run time: 50900 ms
Total hot run time: 48411 ms
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3387234408
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
Review Comment:
这个检查可能必要性不大,这里即使绕过去了。后面的 cascades也会报错
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
Review Comment:
所有的 fedebug,可以换用更精细的 org.apache.doris.common.util.DebugPointUtil
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
Review Comment:
可能遇到 ifnull(x, y) 等函数依然有问题
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java:
##########
@@ -92,6 +97,38 @@ public LogicalSetOperation(PlanType planType, Qualifier qualifier, List<NamedExp
this.qualifier = qualifier;
this.outputs = ImmutableList.copyOf(outputs);
this.regularChildrenOutputs = ImmutableList.copyOf(regularChildrenOutputs);
+ //if (SessionVariable.isFeDebug()) {
+ // checkOutputs(outputs, regularChildrenOutputs)
+ // .ifPresent(msg -> SessionVariable.throwAnalysisExceptionWhenFeDebug(msg));
+ //}
+ }
+
+ // check every slot in outputs has its counterpart in regularChildrenOutputs, and they have the same data type.
+ private Optional<String> checkOutputs(List<NamedExpression> outputs,
Review Comment:
看起来这个函数只在注释里面用了
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/OrExpansion.java:
##########
@@ -176,10 +176,13 @@ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, O
}
//4. union all joins and put producers to context
List<List<SlotReference>> childrenOutputs = joins.stream()
- .map(j -> j.getOutput().stream()
+ .map(j -> j.getOutput().stream() //.map(j -> j.getOutput().stream().distinct()
Review Comment:
多余的注释
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java:
##########
@@ -424,4 +424,12 @@ private static List<NamedExpression> castToCommonType(List<NamedExpression> row,
}
return changed ? castedRow.build() : row;
}
+
+ public LogicalSetOperation withChildrenAndOutputs(List<Plan> children, List<NamedExpression> newOuptuts,
Review Comment:
为什么不是返回 LogicalUnion?
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ColumnPruning.java:
##########
@@ -221,28 +219,21 @@ public Plan visitLogicalUnion(LogicalUnion union, PruneContext context) {
}
LogicalUnion prunedOutputUnion = pruneUnionOutput(union, context);
// start prune children of union
- List<Slot> originOutput = union.getOutput();
Review Comment:
这个修改是因为 union 的列裁剪有bug?
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java:
##########
@@ -0,0 +1,625 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.StatsDerive;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.statistics.ColumnStatistic;
+import org.apache.doris.statistics.Statistics;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * eager aggregation
+ * agg[sum(t1.A) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->T1(A, B, C)
+ * ->T2(D)
+ *
+ * =>
+ * agg[sum(x) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->agg[sum(A) as x, group by B]
+ * ->T1(A, B, C)
+ * ->T2(D)
+ */
+public class EagerAggRewriter extends DefaultPlanRewriter<PushDownAggContext> {
+ private static final double LOWER_AGGREGATE_EFFECT_COEFFICIENT = 10000;
+ private static final double LOW_AGGREGATE_EFFECT_COEFFICIENT = 1000;
+ private static final double MEDIUM_AGGREGATE_EFFECT_COEFFICIENT = 100;
+ private final StatsDerive derive = new StatsDerive(false);
+
+ @Override
+ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, PushDownAggContext context) {
+ boolean toLeft = false;
+ boolean toRight = false;
+ boolean pushHere = false;
+ if (context.getAggFunctions().isEmpty()) {
+ // select t1.v from t1 join t2 on t1.id = t2.id group by t1.v, t2.v
+ // if no agg function, try to push agg to the child which contains all group keys
+ // TODO: consider t1.rows/(t1.id, t1.v).ndv and t2.rows/(t2.id, t2.v).ndv to determine push target
+ if (join.left().getOutputSet().containsAll(context.getGroupKeys())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(context.getGroupKeys())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ } else {
+ for (AggregateFunction aggFunc : context.getAggFunctions()) {
+ if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ }
+ }
+
+ if (pushHere || (toLeft && toRight)) {
Review Comment:
toLeft and toRight 永远是 false?
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java:
##########
@@ -0,0 +1,625 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.StatsDerive;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.statistics.ColumnStatistic;
+import org.apache.doris.statistics.Statistics;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * eager aggregation
+ * agg[sum(t1.A) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->T1(A, B, C)
+ * ->T2(D)
+ *
+ * =>
+ * agg[sum(x) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->agg[sum(A) as x, group by B]
+ * ->T1(A, B, C)
+ * ->T2(D)
+ */
+public class EagerAggRewriter extends DefaultPlanRewriter<PushDownAggContext> {
+ private static final double LOWER_AGGREGATE_EFFECT_COEFFICIENT = 10000;
+ private static final double LOW_AGGREGATE_EFFECT_COEFFICIENT = 1000;
+ private static final double MEDIUM_AGGREGATE_EFFECT_COEFFICIENT = 100;
+ private final StatsDerive derive = new StatsDerive(false);
+
+ @Override
+ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, PushDownAggContext context) {
+ boolean toLeft = false;
+ boolean toRight = false;
+ boolean pushHere = false;
+ if (context.getAggFunctions().isEmpty()) {
+ // select t1.v from t1 join t2 on t1.id = t2.id group by t1.v, t2.v
+ // if no agg function, try to push agg to the child which contains all group keys
+ // TODO: consider t1.rows/(t1.id, t1.v).ndv and t2.rows/(t2.id, t2.v).ndv to determine push target
+ if (join.left().getOutputSet().containsAll(context.getGroupKeys())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(context.getGroupKeys())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ } else {
+ for (AggregateFunction aggFunc : context.getAggFunctions()) {
+ if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
Review Comment:
这里为什么不检查 group by key 了?
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggContext.java:
##########
@@ -0,0 +1,144 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.CascadesContext;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * PushDownAggContext
+ */
+public class PushDownAggContext {
+ public static final int BIG_JOIN_BUILD_SIZE = 400_000;
+ // count(if(...)): if(...) push down as a whole
+ // sum/min/max(if(truePart, elsePart)): if(...) can be split to sum(truePart) and sum(elsePart)
+ public final boolean hasDecomposedAggIf;
+ // When aggFunc(if(...)) is present, pushing down the null-supplemented side of the outer join is avoided.
+ // This is because null values are highly error-prone,
+ // so the push-down operation is not performed during hashCaseWhen.
+ public final boolean hasCaseWhen;
+ private final List<AggregateFunction> aggFunctions;
+ private final List<SlotReference> groupKeys;
+ private final HashMap<AggregateFunction, Alias> aliasMap;
+ private final Set<Slot> aggFunctionsInputSlots;
+
+ // cascadesContext is used for normalizeAgg
+ private final CascadesContext cascadesContext;
+
+ private final boolean passThroughBigJoin;
+
+ /**
+ * constructor
+ */
+ public PushDownAggContext(List<AggregateFunction> aggFunctions,
+ List<SlotReference> groupKeys, Map<AggregateFunction, Alias> aliasMap, CascadesContext cascadesContext,
+ boolean passThroughBigJoin, boolean hasDecomposedAggIf, boolean hasCaseWhen) {
+ this.groupKeys = groupKeys.stream().distinct().collect(Collectors.toList());
+ this.aggFunctions = ImmutableList.copyOf(aggFunctions);
+ this.cascadesContext = cascadesContext;
+
+ HashMap<AggregateFunction, Alias> builtAliasMap = new HashMap<>();
+ if (aliasMap == null) {
+ for (AggregateFunction aggFunction : this.aggFunctions) {
+ builtAliasMap.put(aggFunction, new Alias(aggFunction, aggFunction.getName()));
+ }
+ } else {
+ for (AggregateFunction aggFunction : this.aggFunctions) {
+ Alias alias = aliasMap.get(aggFunction);
+ if (alias == null) {
+ alias = new Alias(aggFunction, aggFunction.getName());
+ }
+ builtAliasMap.put(aggFunction, alias);
+ }
+ }
+ this.aliasMap = builtAliasMap;
+
+ this.aggFunctionsInputSlots = aggFunctions.stream()
+ .flatMap(aggFunction -> aggFunction.getInputSlots().stream())
+ .filter(Slot.class::isInstance)
+ .collect(ImmutableSet.toImmutableSet());
+ this.passThroughBigJoin = passThroughBigJoin;
+ this.hasDecomposedAggIf = hasDecomposedAggIf;
+ this.hasCaseWhen = hasCaseWhen;
+ }
+
+ /**
+ * check validation
+ * @return true, if groupKeys is not empty and no group by key is in aggFunctionsInputSlots
+ */
+ public boolean isValid() {
+ return !groupKeys.isEmpty()
+ && !groupKeys.stream().anyMatch(s -> aggFunctionsInputSlots.contains(s));
Review Comment:
使用 nonMatch。另外这里是为什么呢?sum(a) group by a 好像也没啥问题?
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java:
##########
@@ -92,6 +97,38 @@ public LogicalSetOperation(PlanType planType, Qualifier qualifier, List<NamedExp
this.qualifier = qualifier;
this.outputs = ImmutableList.copyOf(outputs);
this.regularChildrenOutputs = ImmutableList.copyOf(regularChildrenOutputs);
+ //if (SessionVariable.isFeDebug()) {
+ // checkOutputs(outputs, regularChildrenOutputs)
+ // .ifPresent(msg -> SessionVariable.throwAnalysisExceptionWhenFeDebug(msg));
+ //}
Review Comment:
多余的注释需要删掉
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
+ if (aggFunction.arity() > 0 && aggFunction.child(0) instanceof If
+ && !(aggFunction instanceof Count)) {
+ // Decompose Sum/Max/Min(If(cond, a, b)) into separate agg functions.
+ // Count(If(...)) is NOT decomposed here because the top-level
+ // replacement (Count->Sum rollup) cannot match the decomposed
+ // Count(a)/Count(b) as sub-expressions of the original Count(If(cond,a,b)).
+ // Count(If(...)) is pushed down as-is and rolled up normally.
+ If body = (If) (aggFunction).child(0);
+ Set<Slot> valueSlots = Sets.newHashSet(body.getTrueValue().getInputSlots());
+ valueSlots.addAll(body.getFalseValue().getInputSlots());
+ if (body.getCondition().getInputSlots().stream().anyMatch(s -> valueSlots.contains(s))) {
+ // do not push down sum(if a then a else b)
+ return agg;
+ }
+ AggregateFunction aggTrue = (AggregateFunction) aggFunction.withChildren(body.getTrueValue());
+ aggFunctions.add(aggTrue);
+ funcs.add(aggTrue);
+ if (!(body.getFalseValue() instanceof NullLiteral)) {
+ AggregateFunction aggFalse =
+ (AggregateFunction) aggFunction.withChildren(body.getFalseValue());
+ aggFunctions.add(aggFalse);
+ funcs.add(aggFalse);
+ }
+ groupKeys.addAll(body.getCondition().getInputSlots()
+ .stream().map(slot -> (SlotReference) slot).collect(Collectors.toList()));
+ hasDecomposedAggIf = true;
+ } else {
+ aggFunctions.add(aggFunction);
+ funcs.add(aggFunction);
+ }
+
+ } else {
+ return agg;
+ }
+ }
+ }
+
+ groupKeys = groupKeys.stream().distinct().collect(Collectors.toList());
+ if (!checkSubTreePattern(agg.child())) {
+ return agg;
+ }
+
+ PushDownAggContext pushDownContext = new PushDownAggContext(new ArrayList<>(aggFunctions),
+ groupKeys, null, context.getCascadesContext(), false, hasDecomposedAggIf, hasCaseWhen);
+ if (!pushDownContext.isValid()) {
+ return agg;
+ }
+ try {
+ Plan child = agg.child().accept(writer, pushDownContext);
+ if (child != agg.child()) {
+ // agg has been pushed down, rewrite agg output expressions
+ // before: agg[sum(A), by (B)]
+ // ->join(C=D)
+ // ->scan(T1[A...])
+ // ->scan(T2)
+ // after: agg[sum(x), by(B)]
+ // ->join(C=D)
+ // ->agg[sum(A) as x, by(B,C)]
+ // ->scan(T1[A...])
+ // ->scan(T2)
+ List<NamedExpression> newOutputExpressions = new ArrayList<>();
+ //Map<AggregateFunction, AggregateFunction> replaceMap = new HashMap<>();
+ //for (AggregateFunction aggFunc : pushDownContext.getAliasMap().keySet()) {
+ // Alias alias = pushDownContext.getAliasMap().get(aggFunc);
+ // replaceMap.put(aggFunc, (AggregateFunction) aggFunc.withChildren((Expression) alias.toSlot()));
+ //}
+
+ for (NamedExpression ne : agg.getOutputExpressions()) {
+ if (ne instanceof SlotReference) {
+ newOutputExpressions.add(ne);
+ } else {
+ // every expression has its own replaceMap
+ // aggregation(output=[min(A), sum(A)])
+ // --> join
+ // -> T1 [A ...]
+ // -> T2 [...]
+ // =>
+ // aggregation(output=[min(minA), sum(sumA)])
+ // --> join
+ // -> agg(output=[min(A) as minA, sum(A) as sumA])
+ // -> T1 [A ...]
+ // -> T2 [...]
+ // for min(A), replaceMap: A->minA
+ // for sum(A), replaceMap: A->sumA
+ // for count(A), replaceMap: count(A)->sum(countA), because count needs rollup to sum
+ Map<Expression, Expression> replaceMap = new HashMap<>();
+ List<AggregateFunction> relatedAggFunc = aggFunctionsForOutputExpressions.get(ne);
+ for (AggregateFunction func : relatedAggFunc) {
+ Slot pushedDownSlot = pushDownContext.getAliasMap().get(func).toSlot();
+ if (func instanceof Count) {
+ // For count(A), after pushdown we have count(A) as x,
+ // and the top agg should use sum(x) instead of count(x).
+ // Wrap with ifnull(..., 0) because COUNT never returns NULL,
+ // but after pushdown across an outer join, the intermediate count
+ // slot can be NULL (null-extended), making sum(NULL) = NULL.
+ Function rollUpFunc = ((RollUpTrait) func).constructRollUp(pushedDownSlot);
+ replaceMap.put(func, new Nvl(rollUpFunc, new BigIntLiteral(0)));
+ } else if (func.arity() > 0) {
+ // For sum/max/min, replace the child expression with the pushed down slot
+ replaceMap.put(func.child(0), pushedDownSlot);
+ }
+ }
+ NamedExpression replaceAliasExpr = (NamedExpression) ExpressionUtils.replace(ne, replaceMap);
+ replaceAliasExpr = (NamedExpression) ExpressionUtils.rebuildSignature(replaceAliasExpr);
+ newOutputExpressions.add(replaceAliasExpr);
+ }
+ }
+ LogicalAggregate<Plan> eagerAgg =
+ agg.withAggOutputChild(newOutputExpressions, child);
+ NormalizeAggregate normalizeAggregate = new NormalizeAggregate();
+ return normalizeAggregate.normalizeAgg(eagerAgg, Optional.empty(),
+ context.getCascadesContext());
+ }
+ } catch (RuntimeException e) {
+ String msg = "PushDownAggregation failed: " + e.getMessage() + "\n" + agg.treeString();
+ LOG.info(msg, e);
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(msg);
+ }
+ return agg;
+ }
+
+ private boolean checkSubTreePattern(Plan root) {
+ return containsPushDownJoin(root)
+ && checkPlanNodeType(root);
+ }
+
+ private boolean containsPushDownJoin(Plan root) {
+ if (root instanceof LogicalJoin && !((LogicalJoin) root).isMarkJoin()) {
+ return true;
+ }
Review Comment:
这里可以对mark join 和 asof join 等做短路
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
+ if (aggFunction.arity() > 0 && aggFunction.child(0) instanceof If
+ && !(aggFunction instanceof Count)) {
+ // Decompose Sum/Max/Min(If(cond, a, b)) into separate agg functions.
+ // Count(If(...)) is NOT decomposed here because the top-level
+ // replacement (Count->Sum rollup) cannot match the decomposed
+ // Count(a)/Count(b) as sub-expressions of the original Count(If(cond,a,b)).
+ // Count(If(...)) is pushed down as-is and rolled up normally.
+ If body = (If) (aggFunction).child(0);
+ Set<Slot> valueSlots = Sets.newHashSet(body.getTrueValue().getInputSlots());
+ valueSlots.addAll(body.getFalseValue().getInputSlots());
+ if (body.getCondition().getInputSlots().stream().anyMatch(s -> valueSlots.contains(s))) {
+ // do not push down sum(if a then a else b)
+ return agg;
+ }
Review Comment:
这块整段没看懂,为什么 if(x,y,z) 要拆开,以及为什么 if(x, x, y) 不能work?需要更清晰的注释
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BoundFunction.java:
##########
@@ -155,4 +155,8 @@ private Supplier<FunctionSignature> buildSignatureCache(Supplier<FunctionSignatu
});
}
}
+
+ //public void rebuildSignature() {
+ // this.signatureCache = buildSignatureCache(null);
+ //}
Review Comment:
多余的注释
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
+ if (aggFunction.arity() > 0 && aggFunction.child(0) instanceof If
+ && !(aggFunction instanceof Count)) {
+ // Decompose Sum/Max/Min(If(cond, a, b)) into separate agg functions.
+ // Count(If(...)) is NOT decomposed here because the top-level
+ // replacement (Count->Sum rollup) cannot match the decomposed
+ // Count(a)/Count(b) as sub-expressions of the original Count(If(cond,a,b)).
+ // Count(If(...)) is pushed down as-is and rolled up normally.
+ If body = (If) (aggFunction).child(0);
+ Set<Slot> valueSlots = Sets.newHashSet(body.getTrueValue().getInputSlots());
+ valueSlots.addAll(body.getFalseValue().getInputSlots());
+ if (body.getCondition().getInputSlots().stream().anyMatch(s -> valueSlots.contains(s))) {
+ // do not push down sum(if a then a else b)
+ return agg;
+ }
+ AggregateFunction aggTrue = (AggregateFunction) aggFunction.withChildren(body.getTrueValue());
+ aggFunctions.add(aggTrue);
+ funcs.add(aggTrue);
+ if (!(body.getFalseValue() instanceof NullLiteral)) {
+ AggregateFunction aggFalse =
+ (AggregateFunction) aggFunction.withChildren(body.getFalseValue());
+ aggFunctions.add(aggFalse);
+ funcs.add(aggFalse);
+ }
+ groupKeys.addAll(body.getCondition().getInputSlots()
+ .stream().map(slot -> (SlotReference) slot).collect(Collectors.toList()));
+ hasDecomposedAggIf = true;
+ } else {
+ aggFunctions.add(aggFunction);
+ funcs.add(aggFunction);
+ }
+
+ } else {
+ return agg;
+ }
+ }
+ }
+
+ groupKeys = groupKeys.stream().distinct().collect(Collectors.toList());
Review Comment:
这个应该移动到check 之后执行
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ColumnPruning.java:
##########
@@ -221,28 +219,21 @@ public Plan visitLogicalUnion(LogicalUnion union, PruneContext context) {
}
LogicalUnion prunedOutputUnion = pruneUnionOutput(union, context);
// start prune children of union
- List<Slot> originOutput = union.getOutput();
- Set<Slot> prunedOutput = prunedOutputUnion.getOutputSet();
- List<Integer> prunedOutputIndexes = IntStream.range(0, originOutput.size())
- .filter(index -> prunedOutput.contains(originOutput.get(index)))
- .boxed()
- .collect(ImmutableList.toImmutableList());
-
ImmutableList.Builder<Plan> prunedChildren = ImmutableList.builder();
ImmutableList.Builder<List<SlotReference>> prunedChildrenOutputs = ImmutableList.builder();
for (int i = 0; i < prunedOutputUnion.arity(); i++) {
List<SlotReference> regularChildOutputs = prunedOutputUnion.getRegularChildOutput(i);
RoaringBitmap prunedChildOutputExprIds = new RoaringBitmap();
- Builder<SlotReference> prunedChildOutputBuilder
- = ImmutableList.builderWithExpectedSize(regularChildOutputs.size());
- for (Integer index : prunedOutputIndexes) {
- SlotReference slot = regularChildOutputs.get(index);
- prunedChildOutputBuilder.add(slot);
- prunedChildOutputExprIds.add(slot.getExprId().asInt());
- }
-
- List<SlotReference> prunedChildOutput = prunedChildOutputBuilder.build();
+ //Builder<SlotReference> prunedChildOutputBuilder
+ // = ImmutableList.builderWithExpectedSize(regularChildOutputs.size());
+ //for (Integer index : prunedOutputIndexes) {
+ // SlotReference slot = regularChildOutputs.get(index);
+ // prunedChildOutputBuilder.add(slot);
+ // prunedChildOutputExprIds.add(slot.getExprId().asInt());
+ //}
+ regularChildOutputs.forEach(col -> prunedChildOutputExprIds.add(col.getExprId().asInt()));
+ List<SlotReference> prunedChildOutput = regularChildOutputs; //prunedChildOutputBuilder.build();
Review Comment:
多余的注释
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/OrExpansion.java:
##########
@@ -176,10 +176,13 @@ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, O
}
//4. union all joins and put producers to context
List<List<SlotReference>> childrenOutputs = joins.stream()
- .map(j -> j.getOutput().stream()
+ .map(j -> j.getOutput().stream() //.map(j -> j.getOutput().stream().distinct()
.map(SlotReference.class::cast)
.collect(ImmutableList.toImmutableList()))
.collect(ImmutableList.toImmutableList());
+ //LogicalUnion union = new LogicalUnion(Qualifier.ALL,
+ // new ArrayList<>(join.getOutput().stream().distinct().collect(Collectors.toList())),
+ // childrenOutputs, ImmutableList.of(), false, joins);
Review Comment:
多余的注释
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/OrExpansion.java:
##########
@@ -319,8 +322,26 @@ private List<Plan> expandInnerJoin(CascadesContext ctx, Pair<List<Expression>,
LogicalCTEConsumer left = new LogicalCTEConsumer(ctx.getStatementContext().getNextRelationId(),
leftProducer.getCteId(), "", leftProducer);
+ List<NamedExpression> leftOutput = new ArrayList<>();
Review Comment:
这里是因为 or expansion 有bug?
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ColumnPruning.java:
##########
@@ -221,28 +219,21 @@ public Plan visitLogicalUnion(LogicalUnion union, PruneContext context) {
}
LogicalUnion prunedOutputUnion = pruneUnionOutput(union, context);
// start prune children of union
- List<Slot> originOutput = union.getOutput();
- Set<Slot> prunedOutput = prunedOutputUnion.getOutputSet();
- List<Integer> prunedOutputIndexes = IntStream.range(0, originOutput.size())
- .filter(index -> prunedOutput.contains(originOutput.get(index)))
- .boxed()
- .collect(ImmutableList.toImmutableList());
-
ImmutableList.Builder<Plan> prunedChildren = ImmutableList.builder();
ImmutableList.Builder<List<SlotReference>> prunedChildrenOutputs = ImmutableList.builder();
for (int i = 0; i < prunedOutputUnion.arity(); i++) {
List<SlotReference> regularChildOutputs = prunedOutputUnion.getRegularChildOutput(i);
RoaringBitmap prunedChildOutputExprIds = new RoaringBitmap();
- Builder<SlotReference> prunedChildOutputBuilder
- = ImmutableList.builderWithExpectedSize(regularChildOutputs.size());
- for (Integer index : prunedOutputIndexes) {
- SlotReference slot = regularChildOutputs.get(index);
- prunedChildOutputBuilder.add(slot);
- prunedChildOutputExprIds.add(slot.getExprId().asInt());
- }
-
- List<SlotReference> prunedChildOutput = prunedChildOutputBuilder.build();
+ //Builder<SlotReference> prunedChildOutputBuilder
+ // = ImmutableList.builderWithExpectedSize(regularChildOutputs.size());
+ //for (Integer index : prunedOutputIndexes) {
+ // SlotReference slot = regularChildOutputs.get(index);
+ // prunedChildOutputBuilder.add(slot);
+ // prunedChildOutputExprIds.add(slot.getExprId().asInt());
+ //}
Review Comment:
多余的注释
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
+ if (aggFunction.arity() > 0 && aggFunction.child(0) instanceof If
+ && !(aggFunction instanceof Count)) {
+ // Decompose Sum/Max/Min(If(cond, a, b)) into separate agg functions.
+ // Count(If(...)) is NOT decomposed here because the top-level
+ // replacement (Count->Sum rollup) cannot match the decomposed
+ // Count(a)/Count(b) as sub-expressions of the original Count(If(cond,a,b)).
+ // Count(If(...)) is pushed down as-is and rolled up normally.
+ If body = (If) (aggFunction).child(0);
+ Set<Slot> valueSlots = Sets.newHashSet(body.getTrueValue().getInputSlots());
+ valueSlots.addAll(body.getFalseValue().getInputSlots());
+ if (body.getCondition().getInputSlots().stream().anyMatch(s -> valueSlots.contains(s))) {
+ // do not push down sum(if a then a else b)
+ return agg;
+ }
+ AggregateFunction aggTrue = (AggregateFunction) aggFunction.withChildren(body.getTrueValue());
+ aggFunctions.add(aggTrue);
+ funcs.add(aggTrue);
+ if (!(body.getFalseValue() instanceof NullLiteral)) {
+ AggregateFunction aggFalse =
+ (AggregateFunction) aggFunction.withChildren(body.getFalseValue());
+ aggFunctions.add(aggFalse);
+ funcs.add(aggFalse);
+ }
+ groupKeys.addAll(body.getCondition().getInputSlots()
+ .stream().map(slot -> (SlotReference) slot).collect(Collectors.toList()));
+ hasDecomposedAggIf = true;
+ } else {
+ aggFunctions.add(aggFunction);
+ funcs.add(aggFunction);
+ }
+
+ } else {
+ return agg;
+ }
+ }
+ }
+
+ groupKeys = groupKeys.stream().distinct().collect(Collectors.toList());
+ if (!checkSubTreePattern(agg.child())) {
+ return agg;
+ }
+
+ PushDownAggContext pushDownContext = new PushDownAggContext(new ArrayList<>(aggFunctions),
+ groupKeys, null, context.getCascadesContext(), false, hasDecomposedAggIf, hasCaseWhen);
+ if (!pushDownContext.isValid()) {
+ return agg;
+ }
+ try {
+ Plan child = agg.child().accept(writer, pushDownContext);
+ if (child != agg.child()) {
+ // agg has been pushed down, rewrite agg output expressions
+ // before: agg[sum(A), by (B)]
+ // ->join(C=D)
+ // ->scan(T1[A...])
+ // ->scan(T2)
+ // after: agg[sum(x), by(B)]
+ // ->join(C=D)
+ // ->agg[sum(A) as x, by(B,C)]
+ // ->scan(T1[A...])
+ // ->scan(T2)
+ List<NamedExpression> newOutputExpressions = new ArrayList<>();
+ //Map<AggregateFunction, AggregateFunction> replaceMap = new HashMap<>();
+ //for (AggregateFunction aggFunc : pushDownContext.getAliasMap().keySet()) {
+ // Alias alias = pushDownContext.getAliasMap().get(aggFunc);
+ // replaceMap.put(aggFunc, (AggregateFunction) aggFunc.withChildren((Expression) alias.toSlot()));
+ //}
Review Comment:
多余的注释
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java:
##########
@@ -0,0 +1,625 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.StatsDerive;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.statistics.ColumnStatistic;
+import org.apache.doris.statistics.Statistics;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * eager aggregation
+ * agg[sum(t1.A) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->T1(A, B, C)
+ * ->T2(D)
+ *
+ * =>
+ * agg[sum(x) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->agg[sum(A) as x, group by B]
+ * ->T1(A, B, C)
+ * ->T2(D)
+ */
+public class EagerAggRewriter extends DefaultPlanRewriter<PushDownAggContext> {
+ private static final double LOWER_AGGREGATE_EFFECT_COEFFICIENT = 10000;
+ private static final double LOW_AGGREGATE_EFFECT_COEFFICIENT = 1000;
+ private static final double MEDIUM_AGGREGATE_EFFECT_COEFFICIENT = 100;
+ private final StatsDerive derive = new StatsDerive(false);
+
+ @Override
+ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, PushDownAggContext context) {
+ boolean toLeft = false;
+ boolean toRight = false;
+ boolean pushHere = false;
+ if (context.getAggFunctions().isEmpty()) {
+ // select t1.v from t1 join t2 on t1.id = t2.id group by t1.v, t2.v
+ // if no agg function, try to push agg to the child which contains all group keys
+ // TODO: consider t1.rows/(t1.id, t1.v).ndv and t2.rows/(t2.id, t2.v).ndv to determine push target
+ if (join.left().getOutputSet().containsAll(context.getGroupKeys())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(context.getGroupKeys())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ } else {
+ for (AggregateFunction aggFunc : context.getAggFunctions()) {
+ if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ }
+ }
+
+ if (pushHere || (toLeft && toRight)) {
+ if (SessionVariable.isEagerAggregationOnJoin()) {
+ return genAggregate(join, context);
+ } else {
+ return join;
+ }
+ }
+ // Do not push aggregation to the nullable side of outer joins when agg function contains case-when.
+ // CaseWhen expressions may produce non-null values from null-padded rows (e.g., WHEN col IS NULL THEN -54),
+ // so pre-aggregation before the join loses those contributions.
+ if (context.hasDecomposedAggIf || context.hasCaseWhen) {
+ JoinType joinType = join.getJoinType();
+ if (joinType.isFullOuterJoin()) {
+ return join;
+ }
+ if (joinType.isRightOuterJoin()) {
+ toLeft = false;
+ }
+ if (joinType.isLeftOuterJoin()) {
+ toRight = false;
+ }
+ if (!toLeft && !toRight && !pushHere) {
+ return join;
+ }
+ }
+
+ // Do not push count(*)/count(literal)/count(preserved_side_col) to the nullable side of outer joins.
Review Comment:
为什么 count(preserved_side_col) 能推给另一边?
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
+ if (aggFunction.arity() > 0 && aggFunction.child(0) instanceof If
+ && !(aggFunction instanceof Count)) {
+ // Decompose Sum/Max/Min(If(cond, a, b)) into separate agg functions.
+ // Count(If(...)) is NOT decomposed here because the top-level
+ // replacement (Count->Sum rollup) cannot match the decomposed
+ // Count(a)/Count(b) as sub-expressions of the original Count(If(cond,a,b)).
+ // Count(If(...)) is pushed down as-is and rolled up normally.
+ If body = (If) (aggFunction).child(0);
+ Set<Slot> valueSlots = Sets.newHashSet(body.getTrueValue().getInputSlots());
+ valueSlots.addAll(body.getFalseValue().getInputSlots());
+ if (body.getCondition().getInputSlots().stream().anyMatch(s -> valueSlots.contains(s))) {
+ // do not push down sum(if a then a else b)
+ return agg;
+ }
+ AggregateFunction aggTrue = (AggregateFunction) aggFunction.withChildren(body.getTrueValue());
+ aggFunctions.add(aggTrue);
+ funcs.add(aggTrue);
+ if (!(body.getFalseValue() instanceof NullLiteral)) {
+ AggregateFunction aggFalse =
+ (AggregateFunction) aggFunction.withChildren(body.getFalseValue());
+ aggFunctions.add(aggFalse);
+ funcs.add(aggFalse);
+ }
+ groupKeys.addAll(body.getCondition().getInputSlots()
+ .stream().map(slot -> (SlotReference) slot).collect(Collectors.toList()));
+ hasDecomposedAggIf = true;
+ } else {
+ aggFunctions.add(aggFunction);
+ funcs.add(aggFunction);
+ }
+
+ } else {
+ return agg;
+ }
+ }
+ }
+
+ groupKeys = groupKeys.stream().distinct().collect(Collectors.toList());
+ if (!checkSubTreePattern(agg.child())) {
+ return agg;
+ }
+
+ PushDownAggContext pushDownContext = new PushDownAggContext(new ArrayList<>(aggFunctions),
+ groupKeys, null, context.getCascadesContext(), false, hasDecomposedAggIf, hasCaseWhen);
+ if (!pushDownContext.isValid()) {
+ return agg;
+ }
+ try {
+ Plan child = agg.child().accept(writer, pushDownContext);
+ if (child != agg.child()) {
+ // agg has been pushed down, rewrite agg output expressions
+ // before: agg[sum(A), by (B)]
+ // ->join(C=D)
+ // ->scan(T1[A...])
+ // ->scan(T2)
+ // after: agg[sum(x), by(B)]
+ // ->join(C=D)
+ // ->agg[sum(A) as x, by(B,C)]
+ // ->scan(T1[A...])
+ // ->scan(T2)
+ List<NamedExpression> newOutputExpressions = new ArrayList<>();
+ //Map<AggregateFunction, AggregateFunction> replaceMap = new HashMap<>();
+ //for (AggregateFunction aggFunc : pushDownContext.getAliasMap().keySet()) {
+ // Alias alias = pushDownContext.getAliasMap().get(aggFunc);
+ // replaceMap.put(aggFunc, (AggregateFunction) aggFunc.withChildren((Expression) alias.toSlot()));
+ //}
+
+ for (NamedExpression ne : agg.getOutputExpressions()) {
+ if (ne instanceof SlotReference) {
+ newOutputExpressions.add(ne);
+ } else {
+ // every expression has its own replaceMap
+ // aggregation(output=[min(A), sum(A)])
+ // --> join
+ // -> T1 [A ...]
+ // -> T2 [...]
+ // =>
+ // aggregation(output=[min(minA), sum(sumA)])
+ // --> join
+ // -> agg(output=[min(A) as minA, sum(A) as sumA])
+ // -> T1 [A ...]
+ // -> T2 [...]
+ // for min(A), replaceMap: A->minA
+ // for sum(A), replaceMap: A->sumA
+ // for count(A), replaceMap: count(A)->sum(countA), because count needs rollup to sum
+ Map<Expression, Expression> replaceMap = new HashMap<>();
+ List<AggregateFunction> relatedAggFunc = aggFunctionsForOutputExpressions.get(ne);
+ for (AggregateFunction func : relatedAggFunc) {
+ Slot pushedDownSlot = pushDownContext.getAliasMap().get(func).toSlot();
+ if (func instanceof Count) {
+ // For count(A), after pushdown we have count(A) as x,
+ // and the top agg should use sum(x) instead of count(x).
+ // Wrap with ifnull(..., 0) because COUNT never returns NULL,
+ // but after pushdown across an outer join, the intermediate count
+ // slot can be NULL (null-extended), making sum(NULL) = NULL.
+ Function rollUpFunc = ((RollUpTrait) func).constructRollUp(pushedDownSlot);
+ replaceMap.put(func, new Nvl(rollUpFunc, new BigIntLiteral(0)));
+ } else if (func.arity() > 0) {
+ // For sum/max/min, replace the child expression with the pushed down slot
+ replaceMap.put(func.child(0), pushedDownSlot);
+ }
+ }
+ NamedExpression replaceAliasExpr = (NamedExpression) ExpressionUtils.replace(ne, replaceMap);
+ replaceAliasExpr = (NamedExpression) ExpressionUtils.rebuildSignature(replaceAliasExpr);
+ newOutputExpressions.add(replaceAliasExpr);
+ }
+ }
+ LogicalAggregate<Plan> eagerAgg =
+ agg.withAggOutputChild(newOutputExpressions, child);
+ NormalizeAggregate normalizeAggregate = new NormalizeAggregate();
+ return normalizeAggregate.normalizeAgg(eagerAgg, Optional.empty(),
+ context.getCascadesContext());
Review Comment:
为什么需要重新normalize?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3393679623
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java:
##########
@@ -0,0 +1,625 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.StatsDerive;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.statistics.ColumnStatistic;
+import org.apache.doris.statistics.Statistics;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * eager aggregation
+ * agg[sum(t1.A) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->T1(A, B, C)
+ * ->T2(D)
+ *
+ * =>
+ * agg[sum(x) group by t1.B]
+ * ->join(t1.C=t2.D)
+ * ->agg[sum(A) as x, group by B]
+ * ->T1(A, B, C)
+ * ->T2(D)
+ */
+public class EagerAggRewriter extends DefaultPlanRewriter<PushDownAggContext> {
+ private static final double LOWER_AGGREGATE_EFFECT_COEFFICIENT = 10000;
+ private static final double LOW_AGGREGATE_EFFECT_COEFFICIENT = 1000;
+ private static final double MEDIUM_AGGREGATE_EFFECT_COEFFICIENT = 100;
+ private final StatsDerive derive = new StatsDerive(false);
+
+ @Override
+ public Plan visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join, PushDownAggContext context) {
+ boolean toLeft = false;
+ boolean toRight = false;
+ boolean pushHere = false;
+ if (context.getAggFunctions().isEmpty()) {
+ // select t1.v from t1 join t2 on t1.id = t2.id group by t1.v, t2.v
+ // if no agg function, try to push agg to the child which contains all group keys
+ // TODO: consider t1.rows/(t1.id, t1.v).ndv and t2.rows/(t2.id, t2.v).ndv to determine push target
+ if (join.left().getOutputSet().containsAll(context.getGroupKeys())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(context.getGroupKeys())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
+ } else {
+ for (AggregateFunction aggFunc : context.getAggFunctions()) {
+ if (join.left().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toLeft = true;
+ } else if (join.right().getOutputSet().containsAll(aggFunc.getInputSlots())) {
+ toRight = true;
+ } else {
+ pushHere = true;
+ }
Review Comment:
如果没有agg function,就按group key 来决定下推,这相当于agg function就是求 group key的distinct。
这个分支有agg function,就只需要下推agg function,下推后的group key 是join key 和 group key 与被推侧output的交集
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [feat](eager-agg)stabilize and extend eager aggregation pushdown (join/outer join/case-if/count-if) with broad regression baseline refresh [doris]
Posted by "englefly (via GitHub)" <gi...@apache.org>.
englefly commented on code in PR #60757:
URL: https://github.com/apache/doris/pull/60757#discussion_r3392837049
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java:
##########
@@ -0,0 +1,319 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.eageraggregation;
+
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.rules.analysis.NormalizeAggregate;
+import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
+import org.apache.doris.nereids.trees.expressions.CaseWhen;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
+import org.apache.doris.nereids.trees.expressions.functions.agg.RollUpTrait;
+import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.If;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * push down aggregation
+ */
+public class PushDownAggregation extends DefaultPlanRewriter<JobContext> implements CustomRewriter {
+ private static final Logger LOG = LoggerFactory.getLogger(PushDownAggregation.class);
+
+ public final EagerAggRewriter writer = new EagerAggRewriter();
+
+ private final Set<Class> pushDownAggFunctionSet = Sets.newHashSet(
+ Count.class,
+ Sum.class,
+ Max.class,
+ Min.class);
+
+ private final Set<Class> acceptNodeType = Sets.newHashSet(
+ LogicalUnion.class,
+ LogicalProject.class,
+ LogicalFilter.class,
+ LogicalRelation.class,
+ LogicalJoin.class);
+
+ @Override
+ public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+ if (SessionVariable.isFeDebug()) {
+ try {
+ new AdjustNullable(false).rewriteRoot(plan, null);
+ } catch (Exception e) {
+ LOG.warn("(PushDownAggregation) input plan has nullable problem", e);
+ return plan;
+ }
+ }
+ int mode = SessionVariable.getEagerAggregationMode();
+ if (mode < 0) {
+ return plan;
+ } else {
+ Plan result = plan.accept(this, jobContext);
+ if (SessionVariable.isFeDebug()) {
+ result = new AdjustNullable(true).rewriteRoot(result, null);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Plan visitLogicalAggregate(LogicalAggregate<? extends Plan> agg, JobContext context) {
+ Plan newChild = agg.child().accept(this, context);
+ if (newChild != agg.child()) {
+ return agg.withChildren(newChild);
+ }
+
+ if (agg.getSourceRepeat().isPresent()) {
+ return agg;
+ }
+
+ List<SlotReference> groupKeys = new ArrayList<>();
+ for (Expression groupKey : agg.getGroupByExpressions()) {
+ if (groupKey instanceof SlotReference) {
+ groupKeys.add((SlotReference) groupKey);
+ } else {
+ SessionVariable.throwAnalysisExceptionWhenFeDebug(
+ "PushDownAggregation failed: agg is not normalized\n "
+ + agg.treeString());
+ return agg;
+ }
+ }
+
+ Set<AggregateFunction> aggFunctions = Sets.newHashSet();
+ boolean hasDecomposedAggIf = false;
+ boolean hasCaseWhen = false;
+ Map<NamedExpression, List<AggregateFunction>> aggFunctionsForOutputExpressions = Maps.newHashMap();
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj : aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if (aggFunction.isDistinct()) {
+ return agg;
+ }
+ if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ // CaseWhen and If (which CASE WHEN is normalized into) must both be checked.
+ // When an agg function contains an If/CaseWhen whose condition tests IS NULL
+ // (e.g. count(if(col IS NULL, value, NULL))), pushing it to the nullable side
+ // of an outer join produces wrong results: null-extended rows make "col IS NULL"
+ // TRUE at the top level, but the pre-aggregated count slot becomes NULL after
+ // null-extension, and ifnull(sum(NULL), 0) = 0 instead of the correct 1.
+ if (!hasCaseWhen && aggFunction.anyMatch(e -> e instanceof CaseWhen || e instanceof If)) {
+ hasCaseWhen = true;
+ }
Review Comment:
不会有问题. ifnull(x, y) 会作为一个整体下推.
if(a>0, b, c) 拆开推的原因是 a 可以来自左表, b/c 可以来自右表,所以拆开后可以推的范围更大.
ifnull(x, y) 相当于 if(x is null, x, y), 如果条件和 value 来自同一张表,那么也就没有拆开的必要了,只能当做整体下推.
类似的函数也都整体下推
IF(cond, a, b) | 条件为真返回 a,否则返回 b
IFNULL(a, b) | a 不为 NULL 返回 a,否则返回 b(别名:NVL)
CASE WHEN | 多分支条件逻辑,类似 switch-case
COALESCE(a, b, ...) | 返回参数列表中第一个非 NULL 的值
NULLIF(a, b) | 若 a = b 返回 NULL,否则返回 a
LEAST(a, b, ...) | 返回多个参数中的最小值,任一为 NULL 则返回 NULL
GREATEST(a, b, ...) | 返回多个参数中的最大值,任一为 NULL 则返回 NULL
NULL_OR_EMPTY(str) | 字符串为 NULL 或空串时返回 true
NOT_NULL_OR_EMPTY(str) | 字符串既不为 NULL 也不为空串时返回 true
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org