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

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


The following commit(s) were added to refs/heads/branch-2.1 by this push:
     new 8a0d05d9b01 [opt](mtmv) Materialized view partition track supports 
date_trunc and optimize the fail reason (#35562) (#36947)
8a0d05d9b01 is described below

commit 8a0d05d9b01760fab1e2e7d6af5f479272289275
Author: seawinde <149132972+seawi...@users.noreply.github.com>
AuthorDate: Fri Jul 5 15:12:43 2024 +0800

    [opt](mtmv) Materialized view partition track supports date_trunc and 
optimize the fail reason (#35562) (#36947)
    
    cherry pick from master #35562
    commitId: 43d0f191
---
 .../org/apache/doris/mtmv/MTMVPartitionInfo.java   |   6 -
 .../mv/AsyncMaterializationContext.java            |   6 +-
 .../exploration/mv/MaterializedViewUtils.java      | 221 ++++++++++++--
 .../mv/rollup/AggFunctionRollUpHandler.java        |   5 +-
 .../rules/expression/ExpressionNormalization.java  |   2 +
 .../rules/expression/rules/MergeDateTrunc.java     |  78 +++++
 .../org/apache/doris/nereids/trees/TreeNode.java   |   4 +-
 .../trees/expressions/literal/Interval.java        |  37 ++-
 .../commands/info/MTMVPartitionDefinition.java     | 107 ++++---
 .../exploration/mv/MaterializedViewUtilsTest.java  | 172 ++++++++---
 .../expression/ExpressionRewriteTestHelper.java    |   6 +
 .../rules/expression/MergeDateTruncTest.java       |  57 ++++
 .../data/mtmv_p0/test_rollup_partition_mtmv.out    |  22 ++
 .../mtmv_p0/test_rollup_partition_mtmv.groovy      | 325 +++++++++++++++++++++
 14 files changed, 900 insertions(+), 148 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPartitionInfo.java 
b/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPartitionInfo.java
index 7ca1b7e3e63..ff4060f334a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPartitionInfo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPartitionInfo.java
@@ -21,8 +21,6 @@ import org.apache.doris.analysis.Expr;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.common.AnalysisException;
 
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedSet;
 import com.google.gson.annotations.SerializedName;
 
 import java.util.List;
@@ -38,10 +36,6 @@ public class MTMVPartitionInfo {
         SELF_MANAGE
     }
 
-    public static final ImmutableSet<String> MTMV_PARTITION_FUNCTIONS = new 
ImmutableSortedSet.Builder<String>(
-            String.CASE_INSENSITIVE_ORDER).add("date_trunc")
-            .build();
-
     @SerializedName("pt")
     private MTMVPartitionType partitionType;
     @SerializedName("rt")
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java
index 9776673de79..d830e9d41cb 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java
@@ -104,9 +104,9 @@ public class AsyncMaterializationContext extends 
MaterializationContext {
             return Optional.empty();
         }
         RelationId relationId = null;
-        List<LogicalOlapScan> logicalOlapScan = 
this.getScanPlan().collectFirst(LogicalOlapScan.class::isInstance);
-        if (!logicalOlapScan.isEmpty()) {
-            relationId = logicalOlapScan.get(0).getRelationId();
+        Optional<LogicalOlapScan> logicalOlapScan = 
this.getScanPlan().collectFirst(LogicalOlapScan.class::isInstance);
+        if (logicalOlapScan.isPresent()) {
+            relationId = logicalOlapScan.get().getRelationId();
         }
         return Optional.of(Pair.of(relationId, 
normalizeStatisticsColumnExpression(mtmvCache.getStatistics())));
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
index 53f775e2177..5c6a666fdb4 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
@@ -27,12 +27,18 @@ import org.apache.doris.mtmv.MTMVRelatedTableIf;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.StructInfoMap;
+import org.apache.doris.nereids.rules.expression.ExpressionNormalization;
+import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
+import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.ExprId;
 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.WindowExpression;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DateTrunc;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PreAggStatus;
@@ -48,10 +54,12 @@ import 
org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalResultSink;
 import org.apache.doris.nereids.trees.plans.logical.LogicalWindow;
 import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
+import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 
@@ -77,9 +85,11 @@ public class MaterializedViewUtils {
      * @param materializedViewPlan this should be rewritten or analyzed plan, 
should not be physical plan.
      * @param column ref column name.
      */
-    public static Optional<RelatedTableInfo> getRelatedTableInfo(String 
column, Plan materializedViewPlan) {
+    public static RelatedTableInfo getRelatedTableInfo(String column, String 
timeUnit,
+            Plan materializedViewPlan, CascadesContext cascadesContext) {
+
         List<Slot> outputExpressions = materializedViewPlan.getOutput();
-        Slot columnExpr = null;
+        NamedExpression columnExpr = null;
         // get column slot
         for (Slot outputSlot : outputExpressions) {
             if (outputSlot.getName().equalsIgnoreCase(column)) {
@@ -88,14 +98,12 @@ public class MaterializedViewUtils {
             }
         }
         if (columnExpr == null) {
-            return Optional.empty();
-        }
-        if (!(columnExpr instanceof SlotReference)) {
-            return Optional.empty();
+            return RelatedTableInfo.failWith("partition column can not find 
from sql select column");
         }
-        SlotReference columnSlot = (SlotReference) columnExpr;
-        if (!columnSlot.isColumnFromTable()) {
-            return Optional.empty();
+        if (timeUnit != null) {
+            Expression dateTrunc = new DateTrunc(columnExpr, new 
VarcharLiteral(timeUnit));
+            columnExpr = new Alias(dateTrunc);
+            materializedViewPlan = new 
LogicalProject<>(ImmutableList.of(columnExpr), materializedViewPlan);
         }
         // Collect table relation map which is used to identify self join
         List<CatalogRelation> catalogRelationObjs =
@@ -107,19 +115,20 @@ public class MaterializedViewUtils {
         }
         // Check sql pattern
         IncrementCheckerContext checkContext =
-                new IncrementCheckerContext(columnSlot, 
tableCatalogRelationMultimapBuilder.build());
+                new IncrementCheckerContext(columnExpr, 
tableCatalogRelationMultimapBuilder.build(), cascadesContext);
         materializedViewPlan.accept(MaterializedViewIncrementChecker.INSTANCE, 
checkContext);
         Multimap<TableIf, Column> partitionRelatedTableAndColumnMap =
                 checkContext.getPartitionRelatedTableAndColumnMap();
         if (partitionRelatedTableAndColumnMap.isEmpty()) {
-            return Optional.empty();
+            return RelatedTableInfo.failWith(String.format("can't not find 
valid partition track column, because %s",
+                            String.join(",", checkContext.getFailReasons())));
         }
         // TODO support to return only one related table info, support multi 
later
         for (Map.Entry<TableIf, Column> entry : 
partitionRelatedTableAndColumnMap.entries()) {
-            return Optional.of(new RelatedTableInfo(new 
BaseTableInfo(entry.getKey()), true,
-                    entry.getValue().getName()));
+            return RelatedTableInfo.successWith(new 
BaseTableInfo(entry.getKey()), true,
+                    entry.getValue().getName(), 
checkContext.getPartitionExpression().orElseGet(() -> null));
         }
-        return Optional.empty();
+        return RelatedTableInfo.failWith("can't not find valid partition track 
column finally");
     }
 
     /**
@@ -290,10 +299,63 @@ public class MaterializedViewUtils {
             DefaultPlanVisitor<Void, IncrementCheckerContext> {
 
         public static final MaterializedViewIncrementChecker INSTANCE = new 
MaterializedViewIncrementChecker();
+        public static final Set<Class<? extends Expression>> 
SUPPORT_EXPRESSION_TYPES =
+                ImmutableSet.of(DateTrunc.class, SlotReference.class, 
Literal.class);
 
         @Override
         public Void visitLogicalProject(LogicalProject<? extends Plan> 
project, IncrementCheckerContext context) {
-            return visit(project, context);
+            NamedExpression mvPartitionColumn = context.getMvPartitionColumn();
+            List<Slot> output = project.getOutput();
+            if (context.getMvPartitionColumn().isColumnFromTable()) {
+                return visit(project, context);
+            }
+            for (Slot projectSlot : output) {
+                if (!projectSlot.equals(mvPartitionColumn.toSlot())) {
+                    continue;
+                }
+                if (projectSlot.isColumnFromTable()) {
+                    context.setMvPartitionColumn(projectSlot);
+                } else {
+                    // should be only use date_trunc
+                    Expression shuttledExpression =
+                            
ExpressionUtils.shuttleExpressionWithLineage(projectSlot, project, new 
BitSet());
+                    // merge date_trunc
+                    shuttledExpression = new 
ExpressionNormalization().rewrite(shuttledExpression,
+                            new 
ExpressionRewriteContext(context.getCascadesContext()));
+
+                    List<Expression> expressions = 
shuttledExpression.collectToList(Expression.class::isInstance);
+                    for (Expression expression : expressions) {
+                        if (SUPPORT_EXPRESSION_TYPES.stream().noneMatch(
+                                supportExpression -> 
supportExpression.isAssignableFrom(expression.getClass()))) {
+                            context.addFailReason(
+                                    String.format("partition column use 
invalid implicit expression, invalid "
+                                                    + "expression is %s", 
expression));
+                            return null;
+                        }
+                    }
+                    List<DateTrunc> dataTruncExpressions =
+                            
shuttledExpression.collectToList(DateTrunc.class::isInstance);
+                    if (dataTruncExpressions.size() != 1) {
+                        // mv time unit level is little then query
+                        context.addFailReason("partition column time unit 
level should be "
+                                + "greater than sql select column");
+                        return null;
+                    }
+                    Optional<Slot> columnExpr =
+                            
shuttledExpression.getArgument(0).collectFirst(Slot.class::isInstance);
+                    if (!columnExpr.isPresent() || 
!columnExpr.get().isColumnFromTable()) {
+                        context.addFailReason(String.format("partition 
reference column should be direct column "
+                                + "rather then expression except date_trunc, 
columnExpr is %s", columnExpr));
+                        return null;
+                    }
+                    context.setPartitionExpression(shuttledExpression);
+                    context.setMvPartitionColumn(columnExpr.get());
+                }
+                return visit(project, context);
+            }
+            context.addFailReason(String.format("partition reference column 
should be direct column "
+                    + "rather then expression except date_trunc, current 
project is %s", project));
+            return null;
         }
 
         @Override
@@ -305,6 +367,7 @@ public class MaterializedViewUtils {
         public Void visitLogicalJoin(LogicalJoin<? extends Plan, ? extends 
Plan> join,
                 IncrementCheckerContext context) {
             if (join.isMarkJoin()) {
+                context.addFailReason("partition track doesn't support mark 
join");
                 return null;
             }
             Plan left = join.child(0);
@@ -313,7 +376,11 @@ public class MaterializedViewUtils {
                             && slot.isColumnFromTable())
                     .map(slot -> ((SlotReference) slot).getColumn().get())
                     .collect(Collectors.toSet());
-            boolean useLeft = 
leftColumnSet.contains(context.getMvPartitionColumn().getColumn().get());
+            SlotReference contextPartitionColumn = 
getContextPartitionColumn(context);
+            if (contextPartitionColumn == null) {
+                return null;
+            }
+            boolean useLeft = 
leftColumnSet.contains(contextPartitionColumn.getColumn().get());
             JoinType joinType = join.getJoinType();
             if (joinType.isInnerJoin() || joinType.isCrossJoin()) {
                 return visit(join, context);
@@ -326,12 +393,16 @@ public class MaterializedViewUtils {
                     || joinType.isRightSemiJoin()) && !useLeft) {
                 return visit(join.right(), context);
             }
+            context.addFailReason(String.format("partition column is in un 
supported join null generate side, "
+                    + "current join type is %s", joinType));
             return null;
         }
 
         @Override
         public Void visitLogicalRelation(LogicalRelation relation, 
IncrementCheckerContext context) {
             if (!(relation instanceof LogicalCatalogRelation)) {
+                context.addFailReason(String.format("relation should be 
LogicalCatalogRelation, "
+                        + "but now is %s", 
relation.getClass().getSimpleName()));
                 return null;
             }
             LogicalCatalogRelation logicalCatalogRelation = 
(LogicalCatalogRelation) relation;
@@ -339,25 +410,41 @@ public class MaterializedViewUtils {
             // if self join, self join can not partition track now, remove the 
partition column correspondingly
             if (context.getRelationByTable(table).size() > 1) {
                 
context.getPartitionRelatedTableAndColumnMap().removeAll(table);
+                context.addFailReason(String.format("self join doesn't support 
partition update, "
+                        + "self join table name is %s", table.getName()));
                 return null;
             }
             // TODO: 2024/1/31 support only one partition referenced column, 
support multi later
             if (!context.getPartitionRelatedTableAndColumnMap().isEmpty()) {
+                context.addFailReason(String.format("partition track already 
has an related base table column,"
+                        + "track info is %s", 
context.getPartitionRelatedTableAndColumnMap()));
                 return null;
             }
             if (!(table instanceof MTMVRelatedTableIf)) {
+                context.addFailReason(String.format("relation base table is 
not MTMVRelatedTableIf, the table is %s",
+                        table.getName()));
                 return null;
             }
             MTMVRelatedTableIf relatedTable = (MTMVRelatedTableIf) table;
             PartitionType type = relatedTable.getPartitionType();
             if (PartitionType.UNPARTITIONED.equals(type)) {
+                context.addFailReason(String.format("related base table is not 
partition table, the table is %s",
+                        table.getName()));
                 return null;
             }
             Set<Column> partitionColumnSet = new 
HashSet<>(relatedTable.getPartitionColumns());
-            Column mvReferenceColumn = 
context.getMvPartitionColumn().getColumn().get();
+            SlotReference contextPartitionColumn = 
getContextPartitionColumn(context);
+            if (contextPartitionColumn == null) {
+                return null;
+            }
+            Column mvReferenceColumn = 
contextPartitionColumn.getColumn().get();
             if (partitionColumnSet.contains(mvReferenceColumn)
                     && (!mvReferenceColumn.isAllowNull() || 
relatedTable.isPartitionColumnAllowNull())) {
                 context.addTableColumn(table, mvReferenceColumn);
+            } else {
+                context.addFailReason(String.format("related base table 
partition column doesn't contain the mv"
+                                + " partition or partition nullable check 
fail, the mvReferenceColumn is %s",
+                        mvReferenceColumn));
             }
             return visit(relation, context);
         }
@@ -367,6 +454,7 @@ public class MaterializedViewUtils {
                 IncrementCheckerContext context) {
             Set<Expression> groupByExprSet = new 
HashSet<>(aggregate.getGroupByExpressions());
             if (groupByExprSet.isEmpty()) {
+                context.addFailReason("group by sets is empty, doesn't contain 
the target partition");
                 return null;
             }
             Set<Column> originalGroupbyExprSet = new HashSet<>();
@@ -375,7 +463,12 @@ public class MaterializedViewUtils {
                     originalGroupbyExprSet.add(((SlotReference) 
groupExpr).getColumn().get());
                 }
             });
-            if 
(!originalGroupbyExprSet.contains(context.getMvPartitionColumn().getColumn().get()))
 {
+            SlotReference contextPartitionColumn = 
getContextPartitionColumn(context);
+            if (contextPartitionColumn == null) {
+                return null;
+            }
+            if 
(!originalGroupbyExprSet.contains(contextPartitionColumn.getColumn().get())) {
+                context.addFailReason("group by sets doesn't contain the 
target partition");
                 return null;
             }
             return visit(aggregate, context);
@@ -389,6 +482,7 @@ public class MaterializedViewUtils {
             }
             for (NamedExpression namedExpression : windowExpressions) {
                 if (!checkWindowPartition(namedExpression, context)) {
+                    context.addFailReason("window partition sets doesn't 
contain the target partition");
                     return null;
                 }
             }
@@ -421,29 +515,52 @@ public class MaterializedViewUtils {
                         originalPartitionbyExprSet.add(((SlotReference) 
groupExpr).getColumn().get());
                     }
                 });
-                if 
(!originalPartitionbyExprSet.contains(context.getMvPartitionColumn().getColumn().get()))
 {
+                SlotReference contextPartitionColumn = 
getContextPartitionColumn(context);
+                if (contextPartitionColumn == null) {
+                    return false;
+                }
+                if 
(!originalPartitionbyExprSet.contains(contextPartitionColumn.getColumn().get()))
 {
                     return false;
                 }
             }
             return true;
         }
+
+        private SlotReference 
getContextPartitionColumn(IncrementCheckerContext context) {
+            if (!context.getMvPartitionColumn().isColumnFromTable()) {
+                context.addFailReason(String.format("context partition column 
should be slot from column, "
+                                + "context column is %s",
+                        context.getMvPartitionColumn()));
+                return null;
+            }
+            return (SlotReference) context.getMvPartitionColumn();
+        }
     }
 
     private static final class IncrementCheckerContext {
-        private final SlotReference mvPartitionColumn;
+        private NamedExpression mvPartitionColumn;
+        private Optional<Expression> partitionExpression = Optional.empty();
         private final Multimap<TableIdentifier, CatalogRelation> 
tableAndCatalogRelationMap;
         private final Multimap<TableIf, Column> 
partitionRelatedTableAndColumnMap = HashMultimap.create();
+        private final Set<String> failReasons = new HashSet<>();
+        private final CascadesContext cascadesContext;
 
-        public IncrementCheckerContext(SlotReference mvPartitionColumn,
-                Multimap<TableIdentifier, CatalogRelation> 
tableAndCatalogRelationMap) {
+        public IncrementCheckerContext(NamedExpression mvPartitionColumn,
+                Multimap<TableIdentifier, CatalogRelation> 
tableAndCatalogRelationMap,
+                CascadesContext cascadesContext) {
             this.mvPartitionColumn = mvPartitionColumn;
             this.tableAndCatalogRelationMap = tableAndCatalogRelationMap;
+            this.cascadesContext = cascadesContext;
         }
 
-        public SlotReference getMvPartitionColumn() {
+        public NamedExpression getMvPartitionColumn() {
             return mvPartitionColumn;
         }
 
+        public void setMvPartitionColumn(NamedExpression mvPartitionColumn) {
+            this.mvPartitionColumn = mvPartitionColumn;
+        }
+
         public void addTableColumn(TableIf relatedTable, Column 
partitionColumn) {
             partitionRelatedTableAndColumnMap.put(relatedTable, 
partitionColumn);
         }
@@ -459,20 +576,56 @@ public class MaterializedViewUtils {
         public void addTableAndRelation(TableIf tableIf, CatalogRelation 
relation) {
             tableAndCatalogRelationMap.put(new TableIdentifier(tableIf), 
relation);
         }
+
+        public Set<String> getFailReasons() {
+            return failReasons;
+        }
+
+        public void addFailReason(String failReason) {
+            this.failReasons.add(failReason);
+        }
+
+        public CascadesContext getCascadesContext() {
+            return cascadesContext;
+        }
+
+        public Optional<Expression> getPartitionExpression() {
+            return partitionExpression;
+        }
+
+        public void setPartitionExpression(Expression partitionExpression) {
+            this.partitionExpression = 
Optional.ofNullable(partitionExpression);
+        }
     }
 
     /**
      * The related table info that mv relate
      */
     public static final class RelatedTableInfo {
-        private BaseTableInfo tableInfo;
-        private boolean pctPossible;
-        private String column;
-
-        public RelatedTableInfo(BaseTableInfo tableInfo, boolean pctPossible, 
String column) {
+        private final BaseTableInfo tableInfo;
+        private final boolean pctPossible;
+        private final String column;
+        private final Set<String> failReasons = new HashSet<>();
+        // This records the partition expression if exist
+        private Optional<Expression> partitionExpression;
+
+        public RelatedTableInfo(BaseTableInfo tableInfo, boolean pctPossible, 
String column, String failReason,
+                Expression partitionExpression) {
             this.tableInfo = tableInfo;
             this.pctPossible = pctPossible;
             this.column = column;
+            this.failReasons.add(failReason);
+            this.partitionExpression = 
Optional.ofNullable(partitionExpression);
+        }
+
+        public static RelatedTableInfo failWith(String failReason) {
+            return new RelatedTableInfo(null, false, null, failReason,
+                    null);
+        }
+
+        public static RelatedTableInfo successWith(BaseTableInfo tableInfo, 
boolean pctPossible, String column,
+                Expression partitionExpression) {
+            return new RelatedTableInfo(tableInfo, pctPossible, column, "", 
partitionExpression);
         }
 
         public BaseTableInfo getTableInfo() {
@@ -486,5 +639,17 @@ public class MaterializedViewUtils {
         public String getColumn() {
             return column;
         }
+
+        public void addFailReason(String failReason) {
+            this.failReasons.add(failReason);
+        }
+
+        public String getFailReason() {
+            return String.join(",", failReasons);
+        }
+
+        public Optional<Expression> getPartitionExpression() {
+            return partitionExpression;
+        }
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/rollup/AggFunctionRollUpHandler.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/rollup/AggFunctionRollUpHandler.java
index 97229af62c3..250d8a83c26 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/rollup/AggFunctionRollUpHandler.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/rollup/AggFunctionRollUpHandler.java
@@ -24,6 +24,8 @@ 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.RollUpTrait;
 
+import com.google.common.collect.ImmutableList;
+
 import java.util.List;
 import java.util.Set;
 
@@ -63,7 +65,8 @@ public abstract class AggFunctionRollUpHandler {
     protected static List<Expression> extractArguments(Expression 
functionWithAny, Function actualFunction) {
         Set<Object> exprSetToRemove = functionWithAny.collectToSet(expr -> 
!(expr instanceof Any));
         return actualFunction.collectFirst(expr ->
-                exprSetToRemove.stream().noneMatch(exprToRemove -> 
exprToRemove.equals(expr)));
+                        exprSetToRemove.stream().noneMatch(exprToRemove -> 
exprToRemove.equals(expr)))
+                .map(expr -> ImmutableList.of((Expression) 
expr)).orElse(ImmutableList.of());
     }
 
     /**
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionNormalization.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionNormalization.java
index f63ab2eee5f..0e52f2aaaf6 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionNormalization.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionNormalization.java
@@ -23,6 +23,7 @@ import 
org.apache.doris.nereids.rules.expression.rules.DigitalMaskingConvert;
 import org.apache.doris.nereids.rules.expression.rules.FoldConstantRule;
 import org.apache.doris.nereids.rules.expression.rules.InPredicateDedup;
 import 
org.apache.doris.nereids.rules.expression.rules.InPredicateToEqualToRule;
+import org.apache.doris.nereids.rules.expression.rules.MergeDateTrunc;
 import 
org.apache.doris.nereids.rules.expression.rules.NormalizeBinaryPredicatesRule;
 import 
org.apache.doris.nereids.rules.expression.rules.SimplifyArithmeticComparisonRule;
 import org.apache.doris.nereids.rules.expression.rules.SimplifyArithmeticRule;
@@ -53,6 +54,7 @@ public class ExpressionNormalization extends 
ExpressionRewrite {
                 DigitalMaskingConvert.INSTANCE,
                 SimplifyArithmeticComparisonRule.INSTANCE,
                 ConvertAggStateCast.INSTANCE,
+                MergeDateTrunc.INSTANCE,
                 CheckCast.INSTANCE
             )
     );
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/MergeDateTrunc.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/MergeDateTrunc.java
new file mode 100644
index 00000000000..892724cf893
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/MergeDateTrunc.java
@@ -0,0 +1,78 @@
+// 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.expression.rules;
+
+import org.apache.doris.nereids.rules.expression.ExpressionPatternMatcher;
+import org.apache.doris.nereids.rules.expression.ExpressionPatternRuleFactory;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DateTrunc;
+import org.apache.doris.nereids.trees.expressions.literal.Interval.TimeUnit;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Rewrite rule to convert
+ * For example:
+ * date_trunc(date_trunc(data_slot, 'hour'), 'day') -> date_trunc(data_slot, 
'day')
+ */
+public class MergeDateTrunc implements ExpressionPatternRuleFactory {
+
+    public static MergeDateTrunc INSTANCE = new MergeDateTrunc();
+    public static ImmutableSet<TimeUnit> UN_SUPPORT_TIME_UNIT = 
ImmutableSet.of(TimeUnit.WEEK, TimeUnit.QUARTER);
+
+    @Override
+    public List<ExpressionPatternMatcher<? extends Expression>> buildRules() {
+        return ImmutableList.of(
+                matchesTopType(DateTrunc.class)
+                        .when(dateTrunc -> dateTrunc.getArgument(0) instanceof 
DateTrunc)
+                        .then(MergeDateTrunc::rewrite)
+        );
+    }
+
+    private static Expression rewrite(DateTrunc dateTrunc) {
+
+        Expression parentTimeUnitArgument = dateTrunc.getArgument(1);
+        if (!(parentTimeUnitArgument instanceof Literal)) {
+            return dateTrunc;
+        }
+        Optional<TimeUnit> parentTimeUnit = TimeUnit.of(((Literal) 
parentTimeUnitArgument).getStringValue());
+        DateTrunc childDateTrunc = (DateTrunc) dateTrunc.getArgument(0);
+        Expression childTimeUnitArgument = childDateTrunc.getArgument(1);
+        if (!(childTimeUnitArgument instanceof Literal)) {
+            return dateTrunc;
+        }
+        Optional<TimeUnit> childTimeUnit = TimeUnit.of(((Literal) 
childTimeUnitArgument).getStringValue());
+        if (!parentTimeUnit.isPresent() || !childTimeUnit.isPresent()) {
+            return dateTrunc;
+        }
+        if (UN_SUPPORT_TIME_UNIT.contains(parentTimeUnit.get())
+                || UN_SUPPORT_TIME_UNIT.contains(childTimeUnit.get())) {
+            return dateTrunc;
+        }
+        if (parentTimeUnit.get().getLevel() < childTimeUnit.get().getLevel()) {
+            return dateTrunc;
+        }
+        return new DateTrunc(childDateTrunc.getArgument(0), new 
VarcharLiteral(parentTimeUnit.get().toString()));
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
index a4bfab08890..5900584b861 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
@@ -271,7 +271,7 @@ public interface TreeNode<NODE_TYPE extends 
TreeNode<NODE_TYPE>> {
     /**
      * Collect the nodes that satisfied the predicate firstly.
      */
-    default <T> List<T> collectFirst(Predicate<TreeNode<NODE_TYPE>> predicate) 
{
+    default <T> Optional<T> collectFirst(Predicate<TreeNode<NODE_TYPE>> 
predicate) {
         List<TreeNode<NODE_TYPE>> result = new ArrayList<>();
         foreach(node -> {
             if (result.isEmpty() && predicate.test(node)) {
@@ -279,7 +279,7 @@ public interface TreeNode<NODE_TYPE extends 
TreeNode<NODE_TYPE>> {
             }
             return !result.isEmpty();
         });
-        return (List<T>) ImmutableList.copyOf(result);
+        return result.isEmpty() ? Optional.empty() : Optional.of((T) 
result.get(0));
     }
 
     /**
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
index 5cffc0c7fff..454001fb3f1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
@@ -25,6 +25,9 @@ import org.apache.doris.nereids.types.DataType;
 import org.apache.doris.nereids.types.DateType;
 
 import com.google.common.collect.ImmutableList;
+import org.apache.commons.lang3.EnumUtils;
+
+import java.util.Optional;
 
 /**
  * Interval for timestamp calculation.
@@ -61,30 +64,46 @@ public class Interval extends Expression implements 
LeafExpression, AlwaysNotNul
      * Supported time unit.
      */
     public enum TimeUnit {
-        YEAR("YEAR", false),
-        MONTH("MONTH", false),
-        WEEK("WEEK", false),
-        DAY("DAY", false),
-        HOUR("HOUR", true),
-        MINUTE("MINUTE", true),
-        SECOND("SECOND", true);
+        YEAR("YEAR", false, 800),
+        MONTH("MONTH", false, 700),
+        QUARTER("QUARTER", false, 600),
+        WEEK("WEEK", false, 500),
+        DAY("DAY", false, 400),
+        HOUR("HOUR", true, 300),
+        MINUTE("MINUTE", true, 200),
+        SECOND("SECOND", true, 100);
 
         private final String description;
-
         private final boolean isDateTimeUnit;
+        /**
+         * Time unit level, second level is low, year level is high
+         */
+        private final int level;
 
-        TimeUnit(String description, boolean isDateTimeUnit) {
+        TimeUnit(String description, boolean isDateTimeUnit, int level) {
             this.description = description;
             this.isDateTimeUnit = isDateTimeUnit;
+            this.level = level;
         }
 
         public boolean isDateTimeUnit() {
             return isDateTimeUnit;
         }
 
+        public int getLevel() {
+            return level;
+        }
+
         @Override
         public String toString() {
             return description;
         }
+
+        /**
+         * Construct time unit by name
+         */
+        public static Optional<TimeUnit> of(String name) {
+            return 
Optional.ofNullable(EnumUtils.getEnumIgnoreCase(TimeUnit.class, name));
+        }
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/MTMVPartitionDefinition.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/MTMVPartitionDefinition.java
index 09b30063b9d..2c5cbb4649e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/MTMVPartitionDefinition.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/MTMVPartitionDefinition.java
@@ -40,7 +40,10 @@ import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewUtils;
 import 
org.apache.doris.nereids.rules.exploration.mv.MaterializedViewUtils.RelatedTableInfo;
+import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DateTrunc;
 import org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.plans.Plan;
 import 
org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
@@ -51,7 +54,6 @@ import org.apache.doris.qe.SessionVariable;
 import com.google.common.collect.Sets;
 
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -59,6 +61,7 @@ import java.util.stream.Collectors;
  * MTMVPartitionDefinition
  */
 public class MTMVPartitionDefinition {
+    public static final String PARTITION_BY_FUNCTION_NAME = "date_trunc";
     private MTMVPartitionType partitionType;
     private String partitionCol;
     private Expression functionCallExpression;
@@ -78,25 +81,39 @@ public class MTMVPartitionDefinition {
             return mtmvPartitionInfo;
         }
         String partitionColName;
+        String timeUnit;
         if (this.partitionType == MTMVPartitionType.EXPR) {
-            Expr expr;
-            if (functionCallExpression instanceof UnboundFunction) {
-                UnboundFunction function = (UnboundFunction) 
functionCallExpression;
-                expr = new FunctionCallExpr(function.getName(),
-                        new 
FunctionParams(convertToLegacyArguments(function.children())));
+            String functionName = ((UnboundFunction) 
functionCallExpression).getName();
+            if (functionCallExpression instanceof UnboundFunction
+                    && 
functionName.equalsIgnoreCase(PARTITION_BY_FUNCTION_NAME)) {
+                partitionColName = functionCallExpression.getArgument(0) 
instanceof UnboundSlot
+                        ? ((UnboundSlot) 
functionCallExpression.getArgument(0)).getName() : null;
+                timeUnit = 
functionCallExpression.getArguments().get(1).isLiteral()
+                        ? ((Literal) 
functionCallExpression.getArgument(1)).getStringValue() : null;
             } else {
                 throw new AnalysisException(
                         "unsupported auto partition expr " + 
functionCallExpression.toString());
             }
-            partitionColName = getColNameFromExpr(expr);
-            mtmvPartitionInfo.setExpr(expr);
         } else {
             partitionColName = this.partitionCol;
+            timeUnit = null;
         }
         mtmvPartitionInfo.setPartitionCol(partitionColName);
-        RelatedTableInfo relatedTableInfo = getRelatedTableInfo(planner, ctx, 
logicalQuery, partitionColName);
+        RelatedTableInfo relatedTableInfo = getRelatedTableInfo(planner, ctx, 
logicalQuery, partitionColName, timeUnit);
         mtmvPartitionInfo.setRelatedCol(relatedTableInfo.getColumn());
         mtmvPartitionInfo.setRelatedTable(relatedTableInfo.getTableInfo());
+        if (relatedTableInfo.getPartitionExpression().isPresent()) {
+            // Set mv partition expr by relatedTableInfo, this is used for 
partition rollup and so on
+            if 
(relatedTableInfo.getPartitionExpression().get().getExpressionName()
+                    .equalsIgnoreCase(PARTITION_BY_FUNCTION_NAME)) {
+                DateTrunc dateTrunc = (DateTrunc) 
relatedTableInfo.getPartitionExpression().get();
+                // todo use new expression?
+                mtmvPartitionInfo.setExpr(new 
FunctionCallExpr(dateTrunc.getName(),
+                        new 
FunctionParams(convertToLegacyArguments(dateTrunc.children()))));
+                mtmvPartitionInfo.setPartitionType(MTMVPartitionType.EXPR);
+                this.partitionType = MTMVPartitionType.EXPR;
+            }
+        }
         if (this.partitionType == MTMVPartitionType.EXPR) {
             try {
                 
MTMVPartitionExprFactory.getExprService(mtmvPartitionInfo.getExpr()).analyze(mtmvPartitionInfo);
@@ -107,38 +124,10 @@ public class MTMVPartitionDefinition {
         return mtmvPartitionInfo;
     }
 
-    /**
-     * getColNameFromExpr
-     *
-     * @param expr expr
-     * @return String
-     */
-    public static String getColNameFromExpr(Expr expr) {
-        if (!(expr instanceof FunctionCallExpr)) {
-            throw new AnalysisException(
-                    "auto create partition only support function call expr is: 
"
-                            + MTMVPartitionInfo.MTMV_PARTITION_FUNCTIONS);
-        }
-        FunctionCallExpr functionCallExpr = (FunctionCallExpr) expr;
-        List<Expr> paramsExpr = functionCallExpr.getParams().exprs();
-        String name = functionCallExpr.getFnName().getFunction();
-        if (MTMVPartitionInfo.MTMV_PARTITION_FUNCTIONS.contains(name)) {
-            for (Expr param : paramsExpr) {
-                if (param instanceof SlotRef) {
-                    return ((SlotRef) param).getColumnName();
-                }
-            }
-            throw new AnalysisException("can not find colName");
-        } else {
-            throw new AnalysisException(
-                    "auto create partition only support function call expr is: 
"
-                            + MTMVPartitionInfo.MTMV_PARTITION_FUNCTIONS);
-        }
-    }
-
     private RelatedTableInfo getRelatedTableInfo(NereidsPlanner planner, 
ConnectContext ctx, LogicalPlan
             logicalQuery,
-            String partitionColName) {
+            String partitionColName,
+            String timeUnit) {
         CascadesContext cascadesContext = planner.getCascadesContext();
         SessionVariable sessionVariable = 
cascadesContext.getConnectContext().getSessionVariable();
         Set<String> tempDisableRules = 
sessionVariable.getDisableNereidsRuleNames();
@@ -149,12 +138,13 @@ public class MTMVPartitionDefinition {
         try {
             Plan mvRewrittenPlan =
                     planner.plan(logicalQuery, PhysicalProperties.ANY, 
ExplainLevel.REWRITTEN_PLAN);
-            Optional<RelatedTableInfo> relatedTableInfo = MaterializedViewUtils
-                    .getRelatedTableInfo(partitionColName, mvRewrittenPlan);
-            if (!relatedTableInfo.isPresent() || 
!relatedTableInfo.get().isPctPossible()) {
-                throw new AnalysisException("Unable to find a suitable base 
table for partitioning");
+            RelatedTableInfo relatedTableInfo = MaterializedViewUtils
+                    .getRelatedTableInfo(partitionColName, timeUnit, 
mvRewrittenPlan, cascadesContext);
+            if (!relatedTableInfo.isPctPossible()) {
+                throw new AnalysisException(String.format("Unable to find a 
suitable base table for partitioning,"
+                        + " the fail reason is %s", 
relatedTableInfo.getFailReason()));
             }
-            MTMVRelatedTableIf mtmvBaseRealtedTable = 
MTMVUtil.getRelatedTable(relatedTableInfo.get().getTableInfo());
+            MTMVRelatedTableIf mtmvBaseRealtedTable = 
MTMVUtil.getRelatedTable(relatedTableInfo.getTableInfo());
             Set<String> partitionColumnNames = 
Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
             try {
                 
partitionColumnNames.addAll(mtmvBaseRealtedTable.getPartitionColumnNames());
@@ -162,14 +152,14 @@ public class MTMVPartitionDefinition {
                 throw new AnalysisException(e.getMessage(), e);
             }
 
-            if 
(!partitionColumnNames.contains(relatedTableInfo.get().getColumn())) {
-                throw new AnalysisException("error related column: " + 
relatedTableInfo.get().getColumn());
+            if (!partitionColumnNames.contains(relatedTableInfo.getColumn())) {
+                throw new AnalysisException("error related column: " + 
relatedTableInfo.getColumn());
             }
             if (!(mtmvBaseRealtedTable instanceof HMSExternalTable)
                     && partitionColumnNames.size() != 1) {
                 throw new AnalysisException("only hms table support multi 
column partition.");
             }
-            return relatedTableInfo.get();
+            return relatedTableInfo;
         } finally {
             // after operate, roll back the disable rules
             sessionVariable.setDisableNereidsRules(String.join(",", 
tempDisableRules));
@@ -178,15 +168,20 @@ public class MTMVPartitionDefinition {
     }
 
     private static List<Expr> convertToLegacyArguments(List<Expression> 
children) {
-        return children.stream().map(child -> {
-            if (child instanceof UnboundSlot) {
-                return new SlotRef(null, ((UnboundSlot) child).getName());
-            } else if (child instanceof Literal) {
-                return new StringLiteral(((Literal) child).getStringValue());
-            } else {
-                throw new AnalysisException("unsupported argument " + 
child.toString());
-            }
-        }).collect(Collectors.toList());
+        return 
children.stream().map(MTMVPartitionDefinition::convertToLegacyRecursion).collect(Collectors.toList());
+    }
+
+    private static Expr convertToLegacyRecursion(Expression expression) {
+        if (expression instanceof Slot) {
+            return new SlotRef(null, ((Slot) expression).getName());
+        } else if (expression instanceof Literal) {
+            return new StringLiteral(((Literal) expression).getStringValue());
+        } else if (expression instanceof Cast) {
+            // mv partition roll up only need the slot in cast
+            return convertToLegacyRecursion(((Cast) expression).child());
+        } else {
+            throw new AnalysisException("unsupported argument " + 
expression.toString());
+        }
     }
 
     public MTMVPartitionType getPartitionType() {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtilsTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtilsTest.java
index 1ed6b92129a..3878ab742ca 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtilsTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtilsTest.java
@@ -28,8 +28,6 @@ import org.apache.doris.utframe.TestWithFeService;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-import java.util.Optional;
-
 /**
  * Test for materialized view util
  */
@@ -251,8 +249,9 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "on t1.l_orderkey = o_orderkey;",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan);
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
                             checkRelatedTableInfo(relatedTableInfo,
                                     "lineitem_no_data",
                                     "L_SHIPDATE",
@@ -277,8 +276,9 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "ON l.L_PARTKEY = ps.PS_PARTKEY and 
l.L_SUPPKEY = ps.PS_SUPPKEY",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan);
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
                             checkRelatedTableInfo(relatedTableInfo,
                                     "lineitem",
                                     "L_SHIPDATE",
@@ -303,9 +303,10 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "ON l.L_PARTKEY = ps.PS_PARTKEY and 
l.L_SUPPKEY = ps.PS_SUPPKEY",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan);
-                            
Assertions.assertTrue(relatedTableInfo.isPresent());
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            
Assertions.assertTrue(relatedTableInfo.isPctPossible());
                         });
     }
 
@@ -326,8 +327,9 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "GROUP BY l.L_SHIPDATE, o.O_ORDERDATE ",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("ship_data_alias", rewrittenPlan);
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("ship_data_alias", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
                             checkRelatedTableInfo(relatedTableInfo,
                                     "lineitem",
                                     "l_shipdate",
@@ -356,8 +358,9 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "t2.O_ORDERSTATUS;",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan);
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
                             checkRelatedTableInfo(relatedTableInfo,
                                     "lineitem",
                                     "L_SHIPDATE",
@@ -375,8 +378,9 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "        group by l_shipdate, l_orderkey",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_orderkey", rewrittenPlan);
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_orderkey", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
                             checkRelatedTableInfo(relatedTableInfo,
                                     "lineitem_list_partition",
                                     "l_orderkey",
@@ -394,9 +398,12 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "    group by t1.l_shipdate, t1.l_orderkey, 
t1.l_partkey, t1.l_suppkey",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_orderkey", rewrittenPlan);
-                            
Assertions.assertFalse(relatedTableInfo.isPresent());
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_orderkey", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            
Assertions.assertTrue(relatedTableInfo.getFailReason().contains(
+                                    "self join doesn't support partition 
update"));
+                            
Assertions.assertFalse(relatedTableInfo.isPctPossible());
                         });
 
         PlanChecker.from(connectContext)
@@ -407,9 +414,12 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "    group by t1.l_shipdate, t1.l_orderkey, 
t1.l_partkey, t1.l_suppkey",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_orderkey", rewrittenPlan);
-                            
Assertions.assertFalse(relatedTableInfo.isPresent());
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_orderkey", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            
Assertions.assertTrue(relatedTableInfo.getFailReason().contains(
+                                    "self join doesn't support partition 
update"));
+                            
Assertions.assertFalse(relatedTableInfo.isPctPossible());
                         });
 
         PlanChecker.from(connectContext)
@@ -420,9 +430,12 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "    group by t1.l_shipdate, t1.l_orderkey, 
t1.l_partkey, t1.l_suppkey",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_orderkey", rewrittenPlan);
-                            
Assertions.assertFalse(relatedTableInfo.isPresent());
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_orderkey", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            
Assertions.assertTrue(relatedTableInfo.getFailReason().contains(
+                                    "partition column is in un supported join 
null generate side"));
+                            
Assertions.assertFalse(relatedTableInfo.isPctPossible());
                         });
     }
 
@@ -447,8 +460,9 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "t2.O_ORDERSTATUS;",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("o_orderdate", rewrittenPlan);
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("o_orderdate", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
                             checkRelatedTableInfo(relatedTableInfo,
                                     "orders",
                                     "O_ORDERDATE",
@@ -477,9 +491,12 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "t2.O_ORDERSTATUS;",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("o_orderdate", rewrittenPlan);
-                            
Assertions.assertFalse(relatedTableInfo.isPresent());
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("o_orderdate", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            
Assertions.assertTrue(relatedTableInfo.getFailReason().contains(
+                                    "partition column is in un supported join 
null generate side"));
+                            
Assertions.assertFalse(relatedTableInfo.isPctPossible());
                         });
     }
 
@@ -494,9 +511,12 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "ON ps_1.PS_PARTKEY = ps_2.PS_SUPPKEY ",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("PS_SUPPLYCOST", rewrittenPlan);
-                            
Assertions.assertFalse(relatedTableInfo.isPresent());
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("PS_SUPPLYCOST", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            
Assertions.assertTrue(relatedTableInfo.getFailReason().contains(
+                                    "self join doesn't support partition 
update"));
+                            
Assertions.assertFalse(relatedTableInfo.isPctPossible());
                         });
     }
 
@@ -517,8 +537,9 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "ON l.L_PARTKEY = ps.PS_PARTKEY and 
l.L_SUPPKEY = ps.PS_SUPPKEY",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan);
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
                             checkRelatedTableInfo(relatedTableInfo,
                                     "lineitem",
                                     "L_SHIPDATE",
@@ -543,9 +564,73 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + "ON l.L_PARTKEY = ps.PS_PARTKEY and 
l.L_SUPPKEY = ps.PS_SUPPKEY",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("L_SHIPDATE", rewrittenPlan);
-                            
Assertions.assertFalse(relatedTableInfo.isPresent());
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("L_SHIPDATE", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            
Assertions.assertTrue(relatedTableInfo.getFailReason().contains(
+                                    "window partition sets doesn't contain the 
target partition"));
+                            
Assertions.assertFalse(relatedTableInfo.isPctPossible());
+                        });
+    }
+
+    @Test
+    public void testPartitionDateTrunc() {
+        PlanChecker.from(connectContext)
+                .checkExplain("SELECT date_trunc(t1.L_SHIPDATE, 'hour') as 
date_alias, t2.O_ORDERDATE, t1.L_QUANTITY, t2.O_ORDERSTATUS, "
+                                + "count(distinct case when t1.L_SUPPKEY > 0 
then t2.O_ORDERSTATUS else null end) as cnt_1 "
+                                + "from "
+                                + "  (select * from "
+                                + "  lineitem "
+                                + "  where L_SHIPDATE in ('2017-01-30')) t1 "
+                                + "left join "
+                                + "  (select * from "
+                                + "  orders "
+                                + "  where O_ORDERDATE in ('2017-01-30')) t2 "
+                                + "on t1.L_ORDERKEY = t2.O_ORDERKEY "
+                                + "group by "
+                                + "t1.L_SHIPDATE, "
+                                + "t2.O_ORDERDATE, "
+                                + "t1.L_QUANTITY, "
+                                + "t2.O_ORDERSTATUS;",
+                        nereidsPlanner -> {
+                            Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("date_alias", "day",
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            checkRelatedTableInfo(relatedTableInfo,
+                                    "lineitem",
+                                    "L_SHIPDATE",
+                                    true);
+                        });
+    }
+
+    @Test
+    public void testPartitionDateTruncShouldNotTrack() {
+        PlanChecker.from(connectContext)
+                .checkExplain("SELECT date_trunc(t1.L_SHIPDATE, 'day') as 
date_alias, t2.O_ORDERDATE, t1.L_QUANTITY, t2.O_ORDERSTATUS, "
+                                + "count(distinct case when t1.L_SUPPKEY > 0 
then t2.O_ORDERSTATUS else null end) as cnt_1 "
+                                + "from "
+                                + "  (select * from "
+                                + "  lineitem "
+                                + "  where L_SHIPDATE in ('2017-01-30')) t1 "
+                                + "left join "
+                                + "  (select * from "
+                                + "  orders "
+                                + "  where O_ORDERDATE in ('2017-01-30')) t2 "
+                                + "on t1.L_ORDERKEY = t2.O_ORDERKEY "
+                                + "group by "
+                                + "t1.L_SHIPDATE, "
+                                + "t2.O_ORDERDATE, "
+                                + "t1.L_QUANTITY, "
+                                + "t2.O_ORDERSTATUS;",
+                        nereidsPlanner -> {
+                            Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("date_alias", "hour",
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
+                            
Assertions.assertTrue(relatedTableInfo.getFailReason().contains(
+                                    "partition column time unit level should 
be greater than sql select column"));
+                            
Assertions.assertFalse(relatedTableInfo.isPctPossible());
                         });
     }
 
@@ -581,8 +666,9 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                                 + ")t2 on t1.vin_type1 = t2.vin_type2;",
                         nereidsPlanner -> {
                             Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
-                            Optional<RelatedTableInfo> relatedTableInfo =
-                                    
MaterializedViewUtils.getRelatedTableInfo("upgrade_day", rewrittenPlan);
+                            RelatedTableInfo relatedTableInfo =
+                                    
MaterializedViewUtils.getRelatedTableInfo("upgrade_day", null,
+                                            rewrittenPlan, 
nereidsPlanner.getCascadesContext());
                             checkRelatedTableInfo(relatedTableInfo,
                                     "test1",
                                     "upgrade_day",
@@ -630,12 +716,12 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
                         });
     }
 
-    private void checkRelatedTableInfo(Optional<RelatedTableInfo> 
relatedTableInfo,
+    private void checkRelatedTableInfo(RelatedTableInfo relatedTableInfo,
             String expectTableName,
             String expectColumnName,
             boolean pctPossible) {
-        Assertions.assertTrue(relatedTableInfo.isPresent());
-        BaseTableInfo relatedBaseTableInfo = 
relatedTableInfo.get().getTableInfo();
+        Assertions.assertNotNull(relatedTableInfo);
+        BaseTableInfo relatedBaseTableInfo = relatedTableInfo.getTableInfo();
         try {
             TableIf tableIf = Env.getCurrentEnv().getCatalogMgr()
                     
.getCatalogOrAnalysisException(relatedBaseTableInfo.getCtlId())
@@ -645,7 +731,7 @@ public class MaterializedViewUtilsTest extends 
TestWithFeService {
         } catch (Exception exception) {
             Assertions.fail();
         }
-        
Assertions.assertEquals(relatedTableInfo.get().getColumn().toLowerCase(), 
expectColumnName.toLowerCase());
+        Assertions.assertEquals(relatedTableInfo.getColumn().toLowerCase(), 
expectColumnName.toLowerCase());
         Assertions.assertTrue(pctPossible);
     }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
index e245ee2f7e1..ad9db4a8dda 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
@@ -78,6 +78,12 @@ public abstract class ExpressionRewriteTestHelper extends 
ExpressionRewrite {
         Assertions.assertEquals(expectedExpression, rewrittenExpression);
     }
 
+    protected void assertNotRewrite(Expression expression, Expression 
expectedExpression) {
+        expression = typeCoercion(expression);
+        Expression rewrittenExpression = executor.rewrite(expression, context);
+        Assertions.assertNotEquals(expectedExpression, rewrittenExpression);
+    }
+
     protected void assertRewriteAfterTypeCoercion(String expression, String 
expected) {
         Map<String, Slot> mem = Maps.newHashMap();
         Expression needRewriteExpression = PARSER.parseExpression(expression);
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/MergeDateTruncTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/MergeDateTruncTest.java
new file mode 100644
index 00000000000..945c647fcb5
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/MergeDateTruncTest.java
@@ -0,0 +1,57 @@
+// 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.expression;
+
+import org.apache.doris.nereids.rules.expression.rules.MergeDateTrunc;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DateTrunc;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
+import org.apache.doris.nereids.types.DateV2Type;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Test;
+
+/**
+ * merge date_trunc test
+ */
+public class MergeDateTruncTest extends ExpressionRewriteTestHelper {
+
+    @Test
+    public void testMergeDateTrunc() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(MergeDateTrunc.INSTANCE)
+        ));
+        Slot dataSlot = new SlotReference("data_slot", DateV2Type.INSTANCE);
+        assertRewrite(new DateTrunc(new DateTrunc(dataSlot, new 
VarcharLiteral("HOUR")),
+                        new VarcharLiteral("DAY")),
+                new DateTrunc(dataSlot, new VarcharLiteral("DAY")));
+
+        assertRewrite(new DateTrunc(new DateTrunc(dataSlot, new 
VarcharLiteral("DAY")),
+                        new VarcharLiteral("YEAR")),
+                new DateTrunc(dataSlot, new VarcharLiteral("YEAR")));
+
+        assertNotRewrite(new DateTrunc(new DateTrunc(dataSlot, new 
VarcharLiteral("HOUR")),
+                        new VarcharLiteral("WEEK")),
+                new DateTrunc(dataSlot, new VarcharLiteral("WEEK")));
+
+        assertNotRewrite(new DateTrunc(new DateTrunc(dataSlot, new 
VarcharLiteral("DAY")),
+                        new VarcharLiteral("QUARTER")),
+                new DateTrunc(dataSlot, new VarcharLiteral("QUARTER")));
+    }
+}
diff --git a/regression-test/data/mtmv_p0/test_rollup_partition_mtmv.out 
b/regression-test/data/mtmv_p0/test_rollup_partition_mtmv.out
index 5552dac72c3..38e59530d8c 100644
--- a/regression-test/data/mtmv_p0/test_rollup_partition_mtmv.out
+++ b/regression-test/data/mtmv_p0/test_rollup_partition_mtmv.out
@@ -4,6 +4,18 @@
 2      2020-01-02
 3      2020-02-01
 
+-- !date_list_month_partition_by_column --
+2020-01-01     1       2020-01-01
+2020-01-01     2       2020-01-02
+2020-02-01     3       2020-02-01
+
+-- !date_list_month_level --
+2020-01-01     1       2020-01-01
+2020-01-02     2       2020-01-02
+2020-02-01     3       2020-02-01
+
+-- !date_list_year_partition_by_column --
+
 -- !string_list_month --
 1      2020==01==01
 2      2020==01==02
@@ -14,3 +26,13 @@
 2      2020-01-02
 3      2020-02-01
 
+-- !date_range_month_partition_by_column --
+2020-01-01     1       2020-01-01
+2020-01-01     2       2020-01-02
+2020-02-01     3       2020-02-01
+
+-- !date_range_month_level --
+2020-01-01
+2020-01-02
+2020-02-01
+
diff --git a/regression-test/suites/mtmv_p0/test_rollup_partition_mtmv.groovy 
b/regression-test/suites/mtmv_p0/test_rollup_partition_mtmv.groovy
index 3a1bfe9f4fd..d0a30e840c9 100644
--- a/regression-test/suites/mtmv_p0/test_rollup_partition_mtmv.groovy
+++ b/regression-test/suites/mtmv_p0/test_rollup_partition_mtmv.groovy
@@ -69,6 +69,82 @@ suite("test_rollup_partition_mtmv") {
     waitingMTMVTaskFinished(jobName)
     order_qt_date_list_month "SELECT * FROM ${mvName} order by k1,k2"
 
+    sql """drop materialized view if exists ${mvName};"""
+    sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (month_alias)
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'month') as month_alias, * FROM 
${tableName};
+    """
+    def date_list_month_partitions = sql """show partitions from ${mvName}"""
+    logger.info("showPartitionsResult: " + 
date_list_month_partitions.toString())
+    assertEquals(2, date_list_month_partitions.size())
+    waitingMTMVTaskFinished(getJobName(dbName, mvName))
+    order_qt_date_list_month_partition_by_column "SELECT * FROM ${mvName}"
+
+    sql """drop materialized view if exists ${mvName};"""
+    sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(month_alias, 'month'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'day') as month_alias, * FROM ${tableName};
+    """
+    def date_list_month_partitions_level = sql """show partitions from 
${mvName}"""
+    logger.info("showPartitionsResult: " + 
date_list_month_partitions_level.toString())
+    assertEquals(2, date_list_month_partitions_level.size())
+    waitingMTMVTaskFinished(getJobName(dbName, mvName))
+    order_qt_date_list_month_level "SELECT * FROM ${mvName}"
+
+    // mv partition level should be higher or equal then query, should fail
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(month_alias, 'day'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'month') as month_alias, * FROM 
${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+        assertTrue(e.getMessage().contains("partition column time unit level 
should be greater than sql select column"))
+    }
+
+    // mv partition use a column not in mv sql select, should fail
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(`k2`, 'month'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'day') as month_alias FROM ${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+        assertTrue(e.getMessage().contains("partition column can not find from 
sql select column"))
+    }
+
     sql """drop materialized view if exists ${mvName};"""
     // list date year
     sql """
@@ -86,6 +162,23 @@ suite("test_rollup_partition_mtmv") {
     logger.info("showPartitionsResult: " + showPartitionsResult.toString())
     assertEquals(1, showPartitionsResult.size())
 
+    sql """drop materialized view if exists ${mvName};"""
+    // list date year
+    sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (year_alias)
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'year') as year_alias, * FROM ${tableName};
+    """
+    def date_list_year_partitions = sql """show partitions from ${mvName}"""
+    assertEquals(1, date_list_year_partitions.size())
+    order_qt_date_list_year_partition_by_column "SELECT * FROM ${mvName}"
+
     // list string month
     sql """drop table if exists `${tableName}`"""
     sql """drop materialized view if exists ${mvName};"""
@@ -134,6 +227,86 @@ suite("test_rollup_partition_mtmv") {
     order_qt_string_list_month "SELECT * FROM ${mvName} order by k1,k2"
 
 
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(month_alias, 'month'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'day') as month_alias, * FROM ${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+        assertTrue(e.getMessage().contains("partition column use invalid 
implicit expression"))
+    }
+
+    // mv partition level should be higher or equal then query, should fail
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(month_alias, 'day'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'month') as month_alias, * FROM 
${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+        assertTrue(e.getMessage().contains("partition column use invalid 
implicit expression"))
+    }
+
+    // mv partition use a column not in mv sql select, should fail
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(`k2`, 'month'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'day') as month_alias FROM ${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+        assertTrue(e.getMessage().contains("partition column can not find from 
sql select column"))
+    }
+
+    // mv partition column type is date, base table is string, partition 
mapping fail
+    // support later
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (month_alias)
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1',
+            'partition_date_format'='%Y==%m==%d'
+            )
+            AS
+            SELECT date_trunc(`k2`,'month') as month_alias, * FROM 
${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+    }
+
     // range date month
     sql """drop table if exists `${tableName}`"""
     sql """drop materialized view if exists ${mvName};"""
@@ -180,6 +353,86 @@ suite("test_rollup_partition_mtmv") {
     waitingMTMVTaskFinished(jobName)
     order_qt_date_range_month "SELECT * FROM ${mvName} order by k1,k2"
 
+    sql """drop materialized view if exists ${mvName};"""
+    sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (month_alias)
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'month') as month_alias, * FROM 
${tableName};
+    """
+    def date_range_month_partitions = sql """show partitions from ${mvName}"""
+    logger.info("showPartitionsResult: " + 
date_range_month_partitions.toString())
+    assertEquals(2, date_range_month_partitions.size())
+
+    jobName = getJobName(dbName, mvName);
+    log.info(jobName)
+    waitingMTMVTaskFinished(jobName)
+    order_qt_date_range_month_partition_by_column "SELECT * FROM ${mvName}"
+
+    sql """drop materialized view if exists ${mvName};"""
+    sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(day_alias, 'month'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'day') as day_alias FROM ${tableName};
+    """
+    def date_range_month_partitions_level = sql """show partitions from 
${mvName}"""
+    logger.info("showPartitionsResult: " + 
date_range_month_partitions_level.toString())
+    assertEquals(2, date_range_month_partitions_level.size())
+    waitingMTMVTaskFinished(getJobName(dbName, mvName))
+    order_qt_date_range_month_level "SELECT * FROM ${mvName}"
+
+    // mv partition level should be higher or equal then query, should fail
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(month_alias, 'day'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'month') as month_alias, * FROM 
${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+        assertTrue(e.getMessage().contains("partition column time unit level 
should be greater than sql select column"))
+    }
+
+    // mv partition use a column not in mv sql select, should fail
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(`k2`, 'month'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'day') as day_alias FROM ${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+        assertTrue(e.getMessage().contains("partition column can not find from 
sql select column"))
+    }
+
+
     // not support MAXVALUE
     sql """drop table if exists `${tableName}`"""
     sql """drop materialized view if exists ${mvName};"""
@@ -217,6 +470,24 @@ suite("test_rollup_partition_mtmv") {
         log.info(e.getMessage())
     }
 
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+            CREATE MATERIALIZED VIEW ${mvName}
+                BUILD DEFERRED REFRESH AUTO ON MANUAL
+                partition by (month_alias)
+                DISTRIBUTED BY RANDOM BUCKETS 2
+                PROPERTIES (
+                'replication_num' = '1'
+                )
+                AS
+                SELECT date_trunc(`k2`,'month') as month_alias, * FROM 
${tableName};
+            """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+    }
+
 
     // range not support  other data type
     sql """drop table if exists `${tableName}`"""
@@ -253,6 +524,24 @@ suite("test_rollup_partition_mtmv") {
         log.info(e.getMessage())
     }
 
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+            CREATE MATERIALIZED VIEW ${mvName}
+                BUILD DEFERRED REFRESH AUTO ON MANUAL
+                partition by (month_alias)
+                DISTRIBUTED BY RANDOM BUCKETS 2
+                PROPERTIES (
+                'replication_num' = '1'
+                )
+                AS
+                SELECT date_trunc(`k2`,'month') as month_alias, * FROM 
${tableName};
+            """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+    }
+
     // not support trunc hour
     sql """drop table if exists `${tableName}`"""
     sql """drop materialized view if exists ${mvName};"""
@@ -289,4 +578,40 @@ suite("test_rollup_partition_mtmv") {
     } catch (Exception e) {
         log.info(e.getMessage())
     }
+
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+            CREATE MATERIALIZED VIEW ${mvName}
+                BUILD DEFERRED REFRESH AUTO ON MANUAL
+                partition by (hour_alias)
+                DISTRIBUTED BY RANDOM BUCKETS 2
+                PROPERTIES (
+                'replication_num' = '1'
+                )
+                AS
+                SELECT date_trunc(`k2`,'hour') as hour_alias, * FROM 
${tableName};
+            """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+    }
+
+    sql """drop materialized view if exists ${mvName};"""
+    try {
+        sql """
+        CREATE MATERIALIZED VIEW ${mvName}
+            BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+            partition by (date_trunc(miniute_alias, 'hour'))
+            DISTRIBUTED BY RANDOM BUCKETS 2
+            PROPERTIES (
+            'replication_num' = '1'
+            )
+            AS
+            SELECT date_trunc(`k2`,'miniute') as miniute_alias, * FROM 
${tableName};
+        """
+        Assert.fail();
+    } catch (Exception e) {
+        log.info(e.getMessage())
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to