This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new 0cb509daa26 branch-4.0:[fix](mtmv) Fix nested mv rewritten fail when 
bottom mv is partitioned (#57558) (#58277)
0cb509daa26 is described below

commit 0cb509daa260506e0820afc97dcad183a44c27de
Author: seawinde <[email protected]>
AuthorDate: Tue Nov 25 10:04:48 2025 +0800

    branch-4.0:[fix](mtmv) Fix nested mv rewritten fail when bottom mv is 
partitioned (#57558) (#58277)
    
    pr: https://github.com/apache/doris/pull/57558
    commitId:
    
https://github.com/apache/doris/commit/5e431415c9087ba55a14531109083a3cc2153587
    
    ### 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 -->
---
 .../org/apache/doris/mtmv/MTMVRewriteUtil.java     | 16 +++-
 .../rules/exploration/mv/PartitionCompensator.java | 43 +++++------
 .../rules/rewrite/QueryPartitionCollector.java     |  2 +
 .../mv/nested_mtmv/nested_mtmv.groovy              | 85 ++++++++++++++++++++++
 4 files changed, 119 insertions(+), 27 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVRewriteUtil.java 
b/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVRewriteUtil.java
index 47bf20052a9..6dc0bd24da7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVRewriteUtil.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVRewriteUtil.java
@@ -38,6 +38,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 public class MTMVRewriteUtil {
     private static final Logger LOG = 
LogManager.getLogger(MTMVRewriteUtil.class);
@@ -108,6 +109,12 @@ public class MTMVRewriteUtil {
         return res;
     }
 
+    /**
+     * Get mtmv partitions by related table partitions, if relatedPartitions 
is null, return all mtmv partitions
+     * if mtmv is self-manage partition, return all mtmv partitions,
+     * if mtmv is nested mv, return all mtmv partitions,
+     * else return mtmv partitions by relatedPartitions
+     */
     private static Set<String> getMtmvPartitionsByRelatedPartitions(MTMV mtmv, 
MTMVRefreshContext refreshContext,
             Map<List<String>, Set<String>> queryUsedPartitions) throws 
AnalysisException {
         if 
(mtmv.getMvPartitionInfo().getPartitionType().equals(MTMVPartitionType.SELF_MANAGE))
 {
@@ -118,8 +125,15 @@ public class MTMVRewriteUtil {
         if (queryUsedPartitions == null) {
             return mtmv.getPartitionNames();
         }
-        Set<String> res = Sets.newHashSet();
+        // if nested mv, should return directly
         Set<MTMVRelatedTableIf> pctTables = 
mtmv.getMvPartitionInfo().getPctTables();
+        Set<List<String>> pctTableQualifiers = 
pctTables.stream().map(MTMVRelatedTableIf::getFullQualifiers).collect(
+                Collectors.toSet());
+        if (Sets.intersection(pctTableQualifiers, 
queryUsedPartitions.keySet()).isEmpty()) {
+            return mtmv.getPartitionNames();
+        }
+        Set<String> res = Sets.newHashSet();
+
         Map<Pair<MTMVRelatedTableIf, String>, String> relatedToMv = getPctToMv(
                 refreshContext.getPartitionMappings());
         for (Entry<List<String>, Set<String>> entry : 
queryUsedPartitions.entrySet()) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PartitionCompensator.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PartitionCompensator.java
index 7cc649a68b0..22c3540b5cb 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PartitionCompensator.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PartitionCompensator.java
@@ -21,7 +21,6 @@ import org.apache.doris.catalog.MTMV;
 import org.apache.doris.catalog.Partition;
 import org.apache.doris.catalog.PartitionInfo;
 import org.apache.doris.catalog.PartitionType;
-import org.apache.doris.catalog.TableIf;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Pair;
 import org.apache.doris.mtmv.BaseColInfo;
@@ -57,9 +56,10 @@ import java.util.Set;
 public class PartitionCompensator {
 
     public static final Logger LOG = 
LogManager.getLogger(PartitionCompensator.class);
-    // if partition pair is null which means can not get partitions from table 
in QueryPartitionCollector,
-    // we think this table scan query all partitions default
+    // if the partition pair is null which means could not get partitions from 
table in QueryPartitionCollector,
+    // we think the table scans query all-partitions default
     public static final Pair<RelationId, Set<String>> ALL_PARTITIONS = 
Pair.of(null, null);
+    // It means all partitions are used when query
     public static final Collection<Pair<RelationId, Set<String>>> 
ALL_PARTITIONS_LIST =
             ImmutableList.of(ALL_PARTITIONS);
 
@@ -252,33 +252,24 @@ public class PartitionCompensator {
         // if value is not empty, means query some partitions
         Map<List<String>, Set<String>> queryUsedRelatedTablePartitionsMap = 
new HashMap<>();
         tableLoop:
-        for (Map.Entry<List<String>, TableIf> queryUsedTableEntry : 
statementContext.getTables().entrySet()) {
+        for (List<String> queryUsedTable : tableUsedPartitionNameMap.keySet()) 
{
             Set<String> usedPartitionSet = new HashSet<>();
             Collection<Pair<RelationId, Set<String>>> tableUsedPartitions =
-                    
tableUsedPartitionNameMap.get(queryUsedTableEntry.getKey());
-            if (!tableUsedPartitions.isEmpty()) {
-                if (ALL_PARTITIONS_LIST.equals(tableUsedPartitions)) {
-                    
queryUsedRelatedTablePartitionsMap.put(queryUsedTableEntry.getKey(), null);
-                    continue;
-                }
-                for (Pair<RelationId, Set<String>> partitionPair : 
tableUsedPartitions) {
-                    if (!customRelationIdSet.isEmpty()) {
-                        if (ALL_PARTITIONS.equals(partitionPair)) {
-                            continue;
-                        }
-                        if 
(customRelationIdSet.get(partitionPair.key().asInt())) {
-                            usedPartitionSet.addAll(partitionPair.value());
-                        }
-                    } else {
-                        if (ALL_PARTITIONS.equals(partitionPair)) {
-                            
queryUsedRelatedTablePartitionsMap.put(queryUsedTableEntry.getKey(), null);
-                            continue tableLoop;
-                        }
-                        usedPartitionSet.addAll(partitionPair.value());
-                    }
+                    tableUsedPartitionNameMap.get(queryUsedTable);
+            if (ALL_PARTITIONS_LIST.equals(tableUsedPartitions)) {
+                // It means all partitions are used when query
+                queryUsedRelatedTablePartitionsMap.put(queryUsedTable, null);
+                continue;
+            }
+            for (Pair<RelationId, Set<String>> tableUsedPartitionPair : 
tableUsedPartitions) {
+                if (ALL_PARTITIONS.equals(tableUsedPartitionPair)) {
+                    // It means all partitions are used when query
+                    queryUsedRelatedTablePartitionsMap.put(queryUsedTable, 
null);
+                    continue tableLoop;
                 }
+                usedPartitionSet.addAll(tableUsedPartitionPair.value());
             }
-            
queryUsedRelatedTablePartitionsMap.put(queryUsedTableEntry.getKey(), 
usedPartitionSet);
+            queryUsedRelatedTablePartitionsMap.put(queryUsedTable, 
usedPartitionSet);
         }
         return queryUsedRelatedTablePartitionsMap;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/QueryPartitionCollector.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/QueryPartitionCollector.java
index 8488a07cbc7..9907f5270e7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/QueryPartitionCollector.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/QueryPartitionCollector.java
@@ -55,9 +55,11 @@ public class QueryPartitionCollector extends 
DefaultPlanVisitor<Void, CascadesCo
             return null;
         }
         StatementContext statementContext = context.getStatementContext();
+        // Collect relationId to tableId mapping
         Map<Integer, Integer> relationIdToTableId = 
statementContext.getRelationIdToCommonTableIdMap();
         relationIdToTableId.put(catalogRelation.getRelationId().asInt(),
                 
statementContext.getTableId(catalogRelation.getTable()).asInt());
+        // Collect table used partition mapping
         Multimap<List<String>, Pair<RelationId, Set<String>>> 
tableUsedPartitionNameMap = statementContext
                 .getTableUsedPartitionNameMap();
         Set<String> tablePartitions = new HashSet<>();
diff --git 
a/regression-test/suites/nereids_rules_p0/mv/nested_mtmv/nested_mtmv.groovy 
b/regression-test/suites/nereids_rules_p0/mv/nested_mtmv/nested_mtmv.groovy
index 1972c2d505b..30ea11ec956 100644
--- a/regression-test/suites/nereids_rules_p0/mv/nested_mtmv/nested_mtmv.groovy
+++ b/regression-test/suites/nereids_rules_p0/mv/nested_mtmv/nested_mtmv.groovy
@@ -753,4 +753,89 @@ suite("nested_mtmv") {
     mv_rewrite_any_success(sql_5, [mv_3, mv_4, mv_5])
     compare_res(sql_5 + " order by 1,2,3,4,5,6,7,8,9,10,11,12,13")
 
+    sql """
+    drop table if exists sales_partitioned
+    """
+
+    sql """
+CREATE TABLE sales_partitioned (
+product_id INT NOT NULL,
+city VARCHAR(50) NOT NULL,
+sale_date DATE NOT NULL,
+amount DECIMAL(18, 2) NOT NULL
+)
+DUPLICATE KEY(product_id, city, sale_date)
+PARTITION BY RANGE(sale_date) (
+PARTITION p20251001 VALUES [('2025-10-01'), ('2025-10-02')),
+PARTITION p20251002 VALUES [('2025-10-02'), ('2025-10-03')),
+PARTITION p20251003 VALUES [('2025-10-03'), ('2025-10-04')),
+PARTITION p_other VALUES [('2025-10-04'), ('2025-11-01'))
+)
+DISTRIBUTED BY HASH(product_id) BUCKETS 10
+    PROPERTIES (
+    "replication_num" = "1"
+);
+    """
+
+
+    sql """
+INSERT INTO sales_partitioned (product_id, city, sale_date, amount) VALUES
+(101, 'Beijing', '2025-10-01', 100.00), -- p20251001
+(101, 'Shanghai', '2025-10-01', 150.00), -- p20251001
+(102, 'Beijing', '2025-10-02', 200.00), -- p20251002
+(102, 'Shanghai', '2025-10-02', 250.00), -- p20251002
+(101, 'Beijing', '2025-10-03', 120.00), -- p20251003
+(102, 'Shanghai', '2025-10-03', 300.00); -- p20251003
+    """
+
+    create_async_partition_mv(db, "zz_mtmv1", """
+                       SELECT
+                       city,
+                       sale_date,
+                       SUM(amount) AS daily_city_amount
+                       FROM
+                       sales_partitioned
+                       GROUP BY
+                       city, sale_date; 
+    """, "(sale_date)")
+    mv_rewrite_success("""
+                       SELECT
+                       city,
+                       SUM(amount) AS total_city_amount
+                       FROM
+                       sales_partitioned
+                       WHERE
+                       sale_date >= '2025-10-01' AND sale_date <= '2025-10-03'
+                       GROUP BY
+                       city;
+    """, "zz_mtmv1", is_partition_statistics_ready(db, ["zz_mtmv1"]))
+
+    create_async_partition_mv(db, "zz_mtmv2", """
+                       SELECT
+                       city,
+                       date_trunc(sale_date, 'MONTH') AS sale_date,
+                       SUM(daily_city_amount) AS monthly_city_amount
+                       FROM
+                       zz_mtmv1
+                       GROUP BY
+                       city,
+                       date_trunc(sale_date, 'MONTH')
+    """, "(sale_date)")
+
+    mv_rewrite_all_success_without_check_chosen("""
+                       SELECT
+                       date_trunc(sale_date, 'MONTH') AS sale_date,
+                       SUM(daily_city_amount) AS monthly_city_amount
+                       FROM
+                       (SELECT
+                       city,
+                       sale_date,
+                       SUM(amount) AS daily_city_amount
+                       FROM
+                       sales_partitioned
+                       GROUP BY
+                       city, sale_date) as t
+                       GROUP BY
+                       date_trunc(sale_date, 'MONTH'); 
+    """, ["zz_mtmv1", "zz_mtmv2"])
 }


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

Reply via email to