github-actions[bot] commented on code in PR #67362:
URL: https://github.com/apache/doris/pull/67362#discussion_r3905142846


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java:
##########
@@ -183,57 +183,59 @@ protected LogicalAggregate<Plan> aggregateRewriteByView(
                 
materializationContext.getShuttledExprToScanExprMapping().keyPermute(viewToQuerySlotMapping)
                         .flattenMap().get(0);
         Plan queryTopPlan = queryStructInfo.getTopPlan();
-        for (Expression topExpression : queryTopPlan.getOutput()) {
-            if (queryTopPlanFunctionSet.contains(topExpression)) {
-                // if agg function, try to roll up and rewrite
-                Expression rollupedExpression = 
tryRewriteExpression(queryStructInfo, topExpression,
-                        mvExprToMvScanExprQueryBased, aggregateFunctionMode, 
materializationContext,
-                        "Query function roll up fail",
-                        () -> String.format("queryExpression = %s,\n 
mvExprToMvScanExprQueryBased = %s",
-                                topExpression, mvExprToMvScanExprQueryBased));
-                if (rollupedExpression == null) {
-                    return null;
-                }
-                finalOutputExpressions.add(new Alias(rollupedExpression));
-            } else {
-                // if group by dimension, try to rewrite
-                Expression rewrittenGroupByExpression = 
tryRewriteExpression(queryStructInfo, topExpression,
-                        mvExprToMvScanExprQueryBased, groupByMode, 
materializationContext,
-                        "View dimensions doesn't not cover the query 
dimensions",
-                        () -> String.format("mvExprToMvScanExprQueryBased is 
%s,\n queryExpression is %s",
-                                mvExprToMvScanExprQueryBased, topExpression));
-                if (rewrittenGroupByExpression == null) {
-                    // group expr can not rewrite by view
-                    return null;
-                }
-                NamedExpression groupByExpression = rewrittenGroupByExpression 
instanceof NamedExpression
-                        ? (NamedExpression) rewrittenGroupByExpression : new 
Alias(rewrittenGroupByExpression);
-                finalOutputExpressions.add(groupByExpression);
-                finalGroupExpressions.add(groupByExpression);
-            }
-        }
         LogicalAggregate<Plan> queryAggregate = queryTopPlanAndAggPair.value();
         List<Expression> queryGroupByExpressions = 
queryAggregate.getGroupByExpressions();
-        // handle the scene that query top plan not use the group by in query 
bottom aggregate
-        if (needCompensateGroupBy(queryTopPlanGroupBySet, 
queryGroupByExpressions)) {
-            for (Expression expression : queryGroupByExpressions) {
-                if (queryTopPlanGroupBySet.contains(expression)) {
-                    continue;
+        if (queryAggregate.getSourceRepeat().isPresent()) {

Review Comment:
   This branch is now the only consumer of `queryTopPlanGroupBySet` and 
`queryTopPlanFunctionSet`, but they are still computed unconditionally above 
it. `topPlanSplitToGroupAndFunction` traverses the whole top plan once per 
output, so every ordinary aggregate MV candidate pays that cost for data 
immediately discarded. The non-Repeat loop then shuttles each bottom output 
once for the map key and again inside `tryRewriteExpression`, followed by 
another per-output shuttle for the top project. For wide aggregates and 
multiple MV mappings these become several avoidable full-plan lineage walks, 
quadratic in output width for wide plans. Please move the classification into 
the Repeat branch and reuse or batch the non-Repeat lineage results.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java:
##########
@@ -265,7 +267,87 @@ protected LogicalAggregate<Plan> aggregateRewriteByView(
                     queryAggregate.getSourceRepeat().get().getRepeatType(), 
tempRewritedPlan);
             return NormalizeRepeat.doNormalize(repeat);
         }
-        return new LogicalAggregate<>(finalGroupExpressions, 
finalOutputExpressions, tempRewritedPlan);
+
+        // The rewritten aggregate should group by the query bottom 
aggregate's group by expressions,
+        // and its output expressions should be the rewritten group by 
expressions and the rolled up
+        // aggregate functions. The query top plan output expressions are 
recomputed by a project above
+        // the rewritten aggregate, so the projection of a group by key in the 
query top plan (such as
+        // `select cast(date_trunc(ts, 'day') as string) from t group by 
date_trunc(ts, 'day')`) will not
+        // be wrongly treated as a group by key of the rewritten aggregate.
+        // The mapping from the query bottom aggregate output slot to the new 
aggregate output expression
+        // is used to rewrite the query top plan output expressions to 
reference the new aggregate output.
+        Map<Expression, Expression> bottomAggOutputToNewExprMap = new 
HashMap<>();
+        Set<Expression> queryGroupByExpressionSet = new 
HashSet<>(queryGroupByExpressions);
+        for (NamedExpression queryAggregateOutput : 
queryAggregate.getOutputExpressions()) {
+            // The shuttled query bottom aggregate output is used as the map 
key, so that the shuttled
+            // query top plan output expressions can be rewritten to reference 
the new aggregate output.
+            Expression shuttledQueryAggregateOutput = 
ExpressionUtils.shuttleExpressionWithLineage(
+                    queryAggregateOutput, queryTopPlan);
+            if (queryGroupByExpressionSet.contains(queryAggregateOutput)) {
+                // if it is a group by expression, rewrite it to the new 
aggregate group by key
+                Expression rewrittenGroupByExpression = 
tryRewriteExpression(queryStructInfo,
+                        queryAggregateOutput, mvExprToMvScanExprQueryBased, 
groupByMode, materializationContext,
+                        "View dimensions doesn't not cover the query 
dimensions",
+                        () -> String.format("mvExprToMvScanExprQueryBased is 
%s,\n queryExpression is %s",
+                                mvExprToMvScanExprQueryBased, 
queryAggregateOutput));
+                if (rewrittenGroupByExpression == null) {
+                    return null;
+                }
+                NamedExpression groupByOutput = rewrittenGroupByExpression 
instanceof NamedExpression
+                        ? (NamedExpression) rewrittenGroupByExpression : new 
Alias(rewrittenGroupByExpression);
+                finalGroupExpressions.add(groupByOutput);
+                finalOutputExpressions.add(groupByOutput);
+                bottomAggOutputToNewExprMap.put(shuttledQueryAggregateOutput, 
groupByOutput.toSlot());
+            } else {
+                // if it is an aggregate function, try to roll up and rewrite
+                Expression rewrittenFunction = 
tryRewriteExpression(queryStructInfo, queryAggregateOutput,
+                        mvExprToMvScanExprQueryBased, aggregateFunctionMode, 
materializationContext,
+                        "Query function roll up fail",
+                        () -> String.format("queryExpression = %s,\n 
mvExprToMvScanExprQueryBased = %s",
+                                queryAggregateOutput, 
mvExprToMvScanExprQueryBased));
+                if (rewrittenFunction == null) {
+                    return null;
+                }
+                NamedExpression functionOutput = new Alias(rewrittenFunction);
+                finalOutputExpressions.add(functionOutput);
+                bottomAggOutputToNewExprMap.put(shuttledQueryAggregateOutput, 
functionOutput.toSlot());
+            }
+        }
+
+        LogicalAggregate<Plan> rewrittenAggregate =
+                new LogicalAggregate<>(finalGroupExpressions, 
finalOutputExpressions, tempRewritedPlan);
+
+        // rewrite the query top plan output expressions to reference the 
rewritten aggregate output,
+        // the query top plan output slot is shuttled by lineage firstly to 
restore the projection
+        // expression, so a projection of the group by key in the query top 
plan can be recomputed
+        // by a project above the rewritten aggregate.
+        List<NamedExpression> topProjectExpressions = new ArrayList<>();
+        for (Expression topExpression : queryTopPlan.getOutput()) {
+            Expression shuttledTopExpression = 
ExpressionUtils.shuttleExpressionWithLineage(
+                    topExpression, queryTopPlan);
+            Expression replacedExpression = 
ExpressionUtils.replace(shuttledTopExpression,
+                    bottomAggOutputToNewExprMap);
+            topProjectExpressions.add(replacedExpression instanceof 
NamedExpression

Review Comment:
   The map can collapse separately aliased duplicate outputs here. For `SELECT 
SUM(v) AS s1, SUM(v) AS s2`, normalization keeps one bottom `SUM` slot but two 
top ExprIds; both shuttled expressions now replace to the same 
`functionOutput.toSlot()`, and this branch inserts that same Slot twice. The 
rewritten project's output set therefore has size 1 while the query's has size 
2, so `MaterializedViewUtils.rewriteByRules` returns at its output-set-size 
guard before running the required whole-tree normalization and partition 
pruning. In aggregate-on-detail mode this can even leave a derived grouping 
Alias in an unnormalized aggregate. Please preserve a distinct named 
output/ExprId for each top-project position and add a regression that makes the 
skipped pass observable, such as a derived group expression in 
aggregate-on-detail mode or partition pruning, rather than relying only on a 
duplicate-`SUM` result.



-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to