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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java:
##########
@@ -471,6 +470,58 @@ protected List<Plan> doRewrite(StructInfo queryStructInfo, 
CascadesContext casca
         return rewriteResults;
     }
 
+    // Partition compensation only supports one global limit/topN in each 
plan. When this invariant
+    // is not met, return null so the caller skips the rewrite instead of 
changing query semantics.
+    Plan buildPartitionCompensationPlan(Plan rewrittenPlan, Plan 
baseTablePlan, Plan queryPlan) {
+        List<Plan> queryGlobalLimits = queryPlan.collectToList(node -> 
isGlobalLimitOrTopN((Plan) node));
+        List<Plan> rewrittenGlobalLimits = rewrittenPlan.collectToList(
+                node -> isGlobalLimitOrTopN((Plan) node));
+        List<Plan> baseTableGlobalLimits = baseTablePlan.collectToList(
+                node -> isGlobalLimitOrTopN((Plan) node));
+        if (queryGlobalLimits.isEmpty()) {
+            return rewrittenGlobalLimits.isEmpty() && 
baseTableGlobalLimits.isEmpty()
+                    ? buildCompensationUnion(queryPlan, 
Lists.newArrayList(rewrittenPlan, baseTablePlan)) : null;
+        }
+        if (queryGlobalLimits.size() != 1
+                || rewrittenGlobalLimits.size() != 1 || 
baseTableGlobalLimits.size() != 1) {
+            return null;
+        }
+        Plan queryGlobalLimit = queryGlobalLimits.get(0);
+        Plan rewrittenGlobalLimit = rewrittenGlobalLimits.get(0);
+        Plan baseTableGlobalLimit = baseTableGlobalLimits.get(0);
+        // Rewriting can change the limit value and order-key expressions, but 
preserves the outer
+        // operator kind. The offset must still match because it cannot be 
safely adjusted after UNION.
+        if (rewrittenGlobalLimit.getType() != queryGlobalLimit.getType()
+                || baseTableGlobalLimit.getType() != queryGlobalLimit.getType()
+                || getOffset(rewrittenGlobalLimit) != 
getOffset(queryGlobalLimit)
+                || getOffset(baseTableGlobalLimit) != 
getOffset(queryGlobalLimit)) {
+            return null;
+        }
+        Plan compensationUnion = 
buildCompensationUnion(queryGlobalLimit.child(0), Lists.newArrayList(

Review Comment:
   [P1] Preserve branch expression semantics before unioning TopN children
   
   With the default TopN expression pull-up, these are both accepted shapes for 
a query/MV selecting `d, k, v + 1 AS x ORDER BY k LIMIT ...`:
   
   ```text
   query/base:                        rewritten MV:
   Project(v + 1 AS x)               TopN(k_mv)
     TopN(k)                            Project(x_mv)
       Project(v)                         MVScan(x)
   ```
   
   Taking only the two TopN children here maps `x_mv` by ordinal onto the UNION 
output slot named as query `v`; grafting the query tree back then evaluates `v 
+ 1` again. MV-backed rows therefore return `v + 2` while compensated base rows 
return `v + 1` (using BIGINT keeps the types identical, so the current 
count/type/offset and final-output checks all pass). The same invariant is 
required for forwarding reorders and hidden columns, so rejecting only 
non-forwarding aliases is insufficient. Please either normalize each branch to 
a proven common semantic schema before UNION, or reject this asymmetric project 
shape, and cover it with a stale-partition regression.



-- 
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