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

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


The following commit(s) were added to refs/heads/branch-3.1 by this push:
     new 171c0671b49 branch-3.1: [opt](Nereids) support defer materialization 
with project (#52078)
171c0671b49 is described below

commit 171c0671b494930f3950904bea294cfe75e74a20
Author: morrySnow <[email protected]>
AuthorDate: Fri Jun 20 23:42:01 2025 +0800

    branch-3.1: [opt](Nereids) support defer materialization with project 
(#52078)
    
    pick from #47661 #47975 #48747
---
 .../java/org/apache/doris/catalog/OlapTable.java   |   7 +-
 .../glue/translator/PhysicalPlanTranslator.java    |  49 ++--
 .../rules/rewrite/DeferMaterializeTopNResult.java  | 251 +++++++++++++++++++--
 .../limit_push_down/order_push_down.out            | Bin 19633 -> 19793 bytes
 .../lazy_materialize_topn.groovy                   |  73 ++++++
 .../limit_push_down/order_push_down.groovy         |   2 +-
 .../single_table_without_aggregate.groovy          |   3 +
 .../suites/query_p0/sort/topn_2pr_rule.groovy      |   2 +-
 8 files changed, 336 insertions(+), 51 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
index 1248cbd5537..00889d6336c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
@@ -94,6 +94,7 @@ import com.google.common.collect.Sets;
 import com.google.gson.annotations.SerializedName;
 import lombok.Getter;
 import lombok.Setter;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -2900,11 +2901,13 @@ public class OlapTable extends Table implements 
MTMVRelatedTableIf, GsonPostProc
      * @param selectedIndexId the index want to scan
      */
     public TFetchOption generateTwoPhaseReadOption(long selectedIndexId) {
+        boolean useStoreRow = this.storeRowColumn()
+                && 
CollectionUtils.isEmpty(getTableProperty().getCopiedRowStoreColumns());
         TFetchOption fetchOption = new TFetchOption();
-        fetchOption.setFetchRowStore(this.storeRowColumn());
+        fetchOption.setFetchRowStore(useStoreRow);
         fetchOption.setUseTwoPhaseFetch(true);
         fetchOption.setNodesInfo(SystemInfoService.createAliveNodesInfo());
-        if (!this.storeRowColumn()) {
+        if (!useStoreRow) {
             List<TColumn> columnsDesc = Lists.newArrayList();
             getColumnDesc(selectedIndexId, columnsDesc, null, null);
             fetchOption.setColumnDesc(columnsDesc);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
index 8ea9de280d6..ca93b52772e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
@@ -1936,7 +1936,13 @@ public class PhysicalPlanTranslator extends 
DefaultPlanVisitor<PlanFragment, Pla
         List<Expr> allProjectionExprs = Lists.newArrayList();
         List<Slot> slots = null;
         // TODO FE/BE do not support multi-layer-project on MultiDataSink now.
-        if (project.hasMultiLayerProjection() && !(inputFragment instanceof 
MultiCastPlanFragment)) {
+        if (project.hasMultiLayerProjection()
+                && !(inputFragment instanceof MultiCastPlanFragment)
+                // TODO support for two phase read with project, remove it 
after refactor
+                && !(project.child() instanceof PhysicalDeferMaterializeTopN)
+                && !(project.child() instanceof 
PhysicalDeferMaterializeOlapScan
+                || (project.child() instanceof PhysicalFilter
+                && ((PhysicalFilter<?>) project.child()).child() instanceof 
PhysicalDeferMaterializeOlapScan))) {
             int layerCount = project.getMultiLayerProjects().size();
             for (int i = 0; i < layerCount; i++) {
                 List<NamedExpression> layer = 
project.getMultiLayerProjects().get(i);
@@ -2044,37 +2050,28 @@ public class PhysicalPlanTranslator extends 
DefaultPlanVisitor<PlanFragment, Pla
         }
 
         if (inputPlanNode instanceof ScanNode) {
-            TupleDescriptor projectionTuple = null;
-            // slotIdsByOrder is used to ensure the ScanNode's output order is 
same with current Project
-            // if we change the output order in translate project, the upper 
node will receive wrong order
-            // tuple, since they get the order from project.getOutput() not 
scan.getOutput()./
-            projectionTuple = generateTupleDesc(slots,
-                    ((ScanNode) inputPlanNode).getTupleDesc().getTable(), 
context);
-            inputPlanNode.setProjectList(projectionExprs);
-            inputPlanNode.setOutputTupleDesc(projectionTuple);
-
-            // TODO: this is a temporary scheme to support two phase read when 
has project.
-            //  we need to refactor all topn opt into rbo stage.
+            // TODO support for two phase read with project, remove this if 
after refactor
+            if (!(project.child() instanceof PhysicalDeferMaterializeOlapScan
+                    || (project.child() instanceof PhysicalFilter
+                    && ((PhysicalFilter<?>) project.child()).child() 
instanceof PhysicalDeferMaterializeOlapScan))) {
+                TupleDescriptor projectionTuple = generateTupleDesc(slots,
+                        ((ScanNode) inputPlanNode).getTupleDesc().getTable(), 
context);
+                inputPlanNode.setProjectList(projectionExprs);
+                inputPlanNode.setOutputTupleDesc(projectionTuple);
+            }
             if (inputPlanNode instanceof OlapScanNode) {
-                ArrayList<SlotDescriptor> olapScanSlots =
-                        
context.getTupleDesc(inputPlanNode.getTupleIds().get(0)).getSlots();
-                SlotDescriptor lastSlot = 
olapScanSlots.get(olapScanSlots.size() - 1);
-                if (lastSlot.getColumn() != null
-                        && 
lastSlot.getColumn().getName().equals(Column.ROWID_COL)) {
-                    injectRowIdColumnSlot(projectionTuple);
-                    SlotRef slotRef = new SlotRef(lastSlot);
-                    inputPlanNode.getProjectList().add(slotRef);
-                    requiredByProjectSlotIdSet.add(lastSlot.getId());
-                    requiredSlotIdSet.add(lastSlot.getId());
-                }
                 ((OlapScanNode) inputPlanNode).updateRequiredSlots(context, 
requiredByProjectSlotIdSet);
             }
             updateScanSlotsMaterialization((ScanNode) inputPlanNode, 
requiredSlotIdSet,
                     requiredByProjectSlotIdSet, context);
         } else {
-            TupleDescriptor tupleDescriptor = generateTupleDesc(slots, null, 
context);
-            inputPlanNode.setProjectList(projectionExprs);
-            inputPlanNode.setOutputTupleDesc(tupleDescriptor);
+            if (project.child() instanceof PhysicalDeferMaterializeTopN) {
+                inputFragment.setOutputExprs(allProjectionExprs);
+            } else {
+                TupleDescriptor tupleDescriptor = generateTupleDesc(slots, 
null, context);
+                inputPlanNode.setProjectList(projectionExprs);
+                inputPlanNode.setOutputTupleDesc(tupleDescriptor);
+            }
         }
         return inputFragment;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/DeferMaterializeTopNResult.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/DeferMaterializeTopNResult.java
index 6d90d81349d..765081ae016 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/DeferMaterializeTopNResult.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/DeferMaterializeTopNResult.java
@@ -33,17 +33,21 @@ import 
org.apache.doris.nereids.trees.plans.logical.LogicalDeferMaterializeResul
 import 
org.apache.doris.nereids.trees.plans.logical.LogicalDeferMaterializeTopN;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.plans.logical.LogicalResultSink;
 import org.apache.doris.nereids.trees.plans.logical.LogicalTopN;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * rewrite simple top n query to defer materialize slot not use for sort or 
predicate
@@ -54,51 +58,256 @@ public class DeferMaterializeTopNResult implements 
RewriteRuleFactory {
     public List<Rule> buildRules() {
         return ImmutableList.of(
                 RuleType.DEFER_MATERIALIZE_TOP_N_RESULT.build(
-                        logicalResultSink(logicalTopN(logicalOlapScan()))
-                                .when(r -> r.child().getLimit() < 
getTopNOptLimitThreshold())
-                                .whenNot(r -> 
r.child().getOrderKeys().isEmpty())
-                                .when(r -> 
r.child().getOrderKeys().stream().map(OrderKey::getExpr)
-                                        
.allMatch(Expression::isColumnFromTable))
-                                .when(r -> 
r.child().child().getTable().getEnableLightSchemaChange())
-                                .when(r -> 
r.child().child().getTable().isDupKeysOrMergeOnWrite())
-                                .then(r -> deferMaterialize(r, r.child(), 
Optional.empty(), r.child().child()))
+                        logicalResultSink(
+                                logicalTopN(
+                                        logicalOlapScan()
+                                                .when(s -> 
s.getTable().getEnableLightSchemaChange())
+                                                .when(s -> 
s.getTable().isDupKeysOrMergeOnWrite())
+                                ).when(t -> t.getLimit() < 
getTopNOptLimitThreshold())
+                                        .whenNot(t -> 
t.getOrderKeys().isEmpty())
+                                        .when(t -> t.getOrderKeys().stream()
+                                                .map(OrderKey::getExpr)
+                                                
.allMatch(Expression::isColumnFromTable))
+                        ).then(r -> deferMaterialize(r, r.child(),
+                                Optional.empty(), Optional.empty(), 
r.child().child()))
                 ),
                 RuleType.DEFER_MATERIALIZE_TOP_N_RESULT.build(
-                        
logicalResultSink(logicalTopN(logicalFilter(logicalOlapScan())))
-                                .when(r -> r.child().getLimit() < 
getTopNOptLimitThreshold())
-                                .whenNot(r -> 
r.child().getOrderKeys().isEmpty())
-                                .when(r -> 
r.child().getOrderKeys().stream().map(OrderKey::getExpr)
-                                        
.allMatch(Expression::isColumnFromTable))
-                                .when(r -> 
r.child().child().child().getTable().getEnableLightSchemaChange())
-                                .when(r -> 
r.child().child().child().getTable().isDupKeysOrMergeOnWrite())
-                                .then(r -> {
-                                    LogicalFilter<LogicalOlapScan> filter = 
r.child().child();
-                                    return deferMaterialize(r, r.child(), 
Optional.of(filter), filter.child());
-                                })
+                        logicalResultSink(
+                                logicalTopN(
+                                        logicalProject(
+                                                logicalOlapScan()
+                                                        .when(s -> 
s.getTable().getEnableLightSchemaChange())
+                                                        .when(s -> 
s.getTable().isDupKeysOrMergeOnWrite())
+                                        )
+                                ).when(t -> t.getLimit() < 
getTopNOptLimitThreshold())
+                                        .whenNot(t -> 
t.getOrderKeys().isEmpty())
+                                        .when(t -> {
+                                            for (OrderKey orderKey : 
t.getOrderKeys()) {
+                                                if 
(!orderKey.getExpr().isColumnFromTable()) {
+                                                    return false;
+                                                }
+                                                if (!(orderKey.getExpr() 
instanceof SlotReference)) {
+                                                    return false;
+                                                }
+                                                SlotReference slotRef = 
(SlotReference) orderKey.getExpr();
+                                                // do not support alias in 
project now
+                                                if 
(!t.child().getProjects().contains(slotRef)) {
+                                                    return false;
+                                                }
+                                            }
+                                            return true;
+                                        })
+                        ).then(r -> {
+                            LogicalProject<LogicalOlapScan> project = 
r.child().child();
+                            return deferMaterialize(r, r.child(), 
Optional.of(project),
+                                    Optional.empty(), project.child());
+                        })
+                ),
+                RuleType.DEFER_MATERIALIZE_TOP_N_RESULT.build(
+                        logicalResultSink(
+                                logicalTopN(
+                                        logicalFilter(
+                                                logicalOlapScan()
+                                                        .when(s -> 
s.getTable().getEnableLightSchemaChange())
+                                                        .when(s -> 
s.getTable().isDupKeysOrMergeOnWrite())
+                                        )
+                                ).when(t -> t.getLimit() < 
getTopNOptLimitThreshold())
+                                        .whenNot(t -> 
t.getOrderKeys().isEmpty())
+                                        .when(t -> t.getOrderKeys().stream()
+                                                .map(OrderKey::getExpr)
+                                                
.allMatch(Expression::isColumnFromTable))
+                        ).then(r -> {
+                            LogicalFilter<LogicalOlapScan> filter = 
r.child().child();
+                            return deferMaterialize(r, r.child(), 
Optional.empty(),
+                                    Optional.of(filter), filter.child());
+                        })
+                ),
+                RuleType.DEFER_MATERIALIZE_TOP_N_RESULT.build(
+                        logicalResultSink(
+                                logicalTopN(
+                                        logicalProject(
+                                                logicalFilter(
+                                                        logicalOlapScan()
+                                                                .when(s -> 
s.getTable().getEnableLightSchemaChange())
+                                                                .when(s -> 
s.getTable().isDupKeysOrMergeOnWrite())
+                                                )
+                                        )
+                                ).when(t -> t.getLimit() < 
getTopNOptLimitThreshold())
+                                        .whenNot(t -> 
t.getOrderKeys().isEmpty())
+                                        .when(t -> {
+                                            for (OrderKey orderKey : 
t.getOrderKeys()) {
+                                                if 
(!orderKey.getExpr().isColumnFromTable()) {
+                                                    return false;
+                                                }
+                                                if (!(orderKey.getExpr() 
instanceof SlotReference)) {
+                                                    return false;
+                                                }
+                                                SlotReference slotRef = 
(SlotReference) orderKey.getExpr();
+                                                // do not support alias in 
project now
+                                                if 
(!t.child().getProjects().contains(slotRef)) {
+                                                    return false;
+                                                }
+                                            }
+                                            return true;
+                                        })
+                        ).then(r -> {
+                            LogicalProject<LogicalFilter<LogicalOlapScan>> 
project = r.child().child();
+                            LogicalFilter<LogicalOlapScan> filter = 
project.child();
+                            return deferMaterialize(r, r.child(), 
Optional.of(project),
+                                    Optional.of(filter), filter.child());
+                        })
+                ),
+                RuleType.DEFER_MATERIALIZE_TOP_N_RESULT.build(
+                        logicalResultSink(logicalProject(
+                                logicalTopN(
+                                        logicalProject(
+                                                logicalOlapScan()
+                                                        .when(s -> 
s.getTable().getEnableLightSchemaChange())
+                                                        .when(s -> 
s.getTable().isDupKeysOrMergeOnWrite())
+
+                                        )
+                                ).when(t -> t.getLimit() < 
getTopNOptLimitThreshold())
+                                        .whenNot(t -> 
t.getOrderKeys().isEmpty())
+                                        .when(t -> {
+                                            for (OrderKey orderKey : 
t.getOrderKeys()) {
+                                                if 
(!orderKey.getExpr().isColumnFromTable()) {
+                                                    return false;
+                                                }
+                                                if (!(orderKey.getExpr() 
instanceof SlotReference)) {
+                                                    return false;
+                                                }
+                                                SlotReference slotRef = 
(SlotReference) orderKey.getExpr();
+                                                // do not support alias in 
project now
+                                                if 
(!t.child().getProjects().contains(slotRef)) {
+                                                    return false;
+                                                }
+                                            }
+                                            return true;
+                                        })
+                        ).when(project -> 
project.canMergeProjections(project.child().child()))).then(r -> {
+                            LogicalProject<?> upperProject = r.child();
+                            LogicalProject<LogicalOlapScan> bottomProject = 
r.child().child().child();
+                            List<NamedExpression> projections = 
upperProject.mergeProjections(bottomProject);
+                            LogicalProject<?> project = 
upperProject.withProjects(projections);
+                            return deferMaterialize(r, r.child().child(), 
Optional.of(project),
+                                    Optional.empty(), bottomProject.child());
+                        })
+                ),
+                RuleType.DEFER_MATERIALIZE_TOP_N_RESULT.build(
+                        logicalResultSink(logicalProject(
+                                logicalTopN(
+                                        logicalOlapScan()
+                                                .when(s -> 
s.getTable().getEnableLightSchemaChange())
+                                                .when(s -> 
s.getTable().isDupKeysOrMergeOnWrite())
+
+                                ).when(t -> t.getLimit() < 
getTopNOptLimitThreshold())
+                                        .whenNot(t -> 
t.getOrderKeys().isEmpty())
+                                        .when(t -> {
+                                            for (OrderKey orderKey : 
t.getOrderKeys()) {
+                                                if 
(!orderKey.getExpr().isColumnFromTable()) {
+                                                    return false;
+                                                }
+                                                if (!(orderKey.getExpr() 
instanceof SlotReference)) {
+                                                    return false;
+                                                }
+                                            }
+                                            return true;
+                                        })
+                        )).then(r -> deferMaterialize(r, r.child().child(), 
Optional.of(r.child()),
+                                Optional.empty(), r.child().child().child()))
+                ),
+                RuleType.DEFER_MATERIALIZE_TOP_N_RESULT.build(
+                        logicalResultSink(logicalProject(
+                                logicalTopN(
+                                        logicalProject(logicalFilter(
+                                                        logicalOlapScan()
+                                                                .when(s -> 
s.getTable().getEnableLightSchemaChange())
+                                                                .when(s -> 
s.getTable().isDupKeysOrMergeOnWrite())
+                                                )
+                                        )
+                                ).when(t -> t.getLimit() < 
getTopNOptLimitThreshold())
+                                        .whenNot(t -> 
t.getOrderKeys().isEmpty())
+                                        .when(t -> {
+                                            for (OrderKey orderKey : 
t.getOrderKeys()) {
+                                                if 
(!orderKey.getExpr().isColumnFromTable()) {
+                                                    return false;
+                                                }
+                                                if (!(orderKey.getExpr() 
instanceof SlotReference)) {
+                                                    return false;
+                                                }
+                                                SlotReference slotRef = 
(SlotReference) orderKey.getExpr();
+                                                // do not support alias in 
project now
+                                                if 
(!t.child().getProjects().contains(slotRef)) {
+                                                    return false;
+                                                }
+                                            }
+                                            return true;
+                                        })
+                        ).when(project -> 
project.canMergeProjections(project.child().child()))).then(r -> {
+                            LogicalProject<?> upperProject = r.child();
+                            LogicalProject<LogicalFilter<LogicalOlapScan>> 
bottomProject = r.child().child().child();
+                            List<NamedExpression> projections = 
upperProject.mergeProjections(bottomProject);
+                            LogicalProject<?> project = 
upperProject.withProjects(projections);
+                            LogicalFilter<LogicalOlapScan> filter = 
bottomProject.child();
+                            return deferMaterialize(r, r.child().child(), 
Optional.of(project),
+                                    Optional.of(filter), filter.child());
+                        })
                 )
         );
     }
 
     private Plan deferMaterialize(LogicalResultSink<? extends Plan> 
logicalResultSink,
-            LogicalTopN<? extends Plan> logicalTopN, Optional<LogicalFilter<? 
extends Plan>> logicalFilter,
-            LogicalOlapScan logicalOlapScan) {
+            LogicalTopN<? extends Plan> logicalTopN, Optional<LogicalProject<? 
extends Plan>> logicalProject,
+            Optional<LogicalFilter<? extends Plan>> logicalFilter, 
LogicalOlapScan logicalOlapScan) {
         Column rowId = new Column(Column.ROWID_COL, Type.STRING, false, null, 
false, "", "rowid column");
         SlotReference columnId = SlotReference.fromColumn(
                 logicalOlapScan.getTable(), rowId, 
logicalOlapScan.getQualifier());
+        Set<Slot> orderKeys = Sets.newHashSet();
         Set<ExprId> deferredMaterializedExprIds = 
Sets.newHashSet(logicalOlapScan.getOutputExprIdSet());
         logicalFilter.ifPresent(filter -> filter.getConjuncts()
                 .forEach(e -> 
deferredMaterializedExprIds.removeAll(e.getInputSlotExprIds())));
         logicalTopN.getOrderKeys().stream()
                 .map(OrderKey::getExpr)
                 .map(Slot.class::cast)
+                .peek(orderKeys::add)
                 .map(NamedExpression::getExprId)
                 .filter(Objects::nonNull)
                 .forEach(deferredMaterializedExprIds::remove);
+        if (logicalProject.isPresent()) {
+            
deferredMaterializedExprIds.retainAll(logicalProject.get().getInputSlots().stream()
+                    
.map(NamedExpression::getExprId).collect(Collectors.toSet()));
+        }
+        if (deferredMaterializedExprIds.isEmpty()) {
+            // nothing to deferred materialize
+            return null;
+        }
         LogicalDeferMaterializeOlapScan deferOlapScan = new 
LogicalDeferMaterializeOlapScan(
                 logicalOlapScan, deferredMaterializedExprIds, columnId);
         Plan root = logicalFilter.map(f -> 
f.withChildren(deferOlapScan)).orElse(deferOlapScan);
+        Set<Slot> inputSlots = Sets.newHashSet();
+        logicalFilter.ifPresent(filter -> 
inputSlots.addAll(filter.getInputSlots()));
+        if (logicalProject.isPresent()) {
+            ImmutableList.Builder<NamedExpression> requiredSlots = 
ImmutableList.builder();
+            inputSlots.addAll(logicalProject.get().getInputSlots());
+            for (Slot output : root.getOutput()) {
+                if (inputSlots.contains(output) || orderKeys.contains(output)) 
{
+                    requiredSlots.add(output);
+                }
+            }
+            requiredSlots.add(columnId);
+            root = new LogicalProject<>(requiredSlots.build(), root);
+        }
         root = new LogicalDeferMaterializeTopN<>((LogicalTopN<? extends Plan>) 
logicalTopN.withChildren(root),
                 deferredMaterializedExprIds, columnId);
+        if (logicalProject.isPresent()) {
+            // generate projections with the order exactly same as result 
output's
+            Map<Slot, NamedExpression> projectsMap = Maps.newHashMap();
+            logicalProject.get().getProjects().forEach(p -> 
projectsMap.put(p.toSlot(), p));
+            List<NamedExpression> outputProjects = 
logicalResultSink.getOutput().stream()
+                    .map(projectsMap::get)
+                    .collect(ImmutableList.toImmutableList());
+            root = logicalProject.get().withProjectsAndChild(outputProjects, 
root);
+        }
         root = logicalResultSink.withChildren(root);
         return new LogicalDeferMaterializeResultSink<>((LogicalResultSink<? 
extends Plan>) root,
                 logicalOlapScan.getTable(), 
logicalOlapScan.getSelectedIndexId());
diff --git 
a/regression-test/data/nereids_rules_p0/limit_push_down/order_push_down.out 
b/regression-test/data/nereids_rules_p0/limit_push_down/order_push_down.out
index 694e609eaeb..5f0e3fa8099 100644
Binary files 
a/regression-test/data/nereids_rules_p0/limit_push_down/order_push_down.out and 
b/regression-test/data/nereids_rules_p0/limit_push_down/order_push_down.out 
differ
diff --git 
a/regression-test/suites/nereids_rules_p0/defer_materialize_topn/lazy_materialize_topn.groovy
 
b/regression-test/suites/nereids_rules_p0/defer_materialize_topn/lazy_materialize_topn.groovy
new file mode 100644
index 00000000000..cd683b08f28
--- /dev/null
+++ 
b/regression-test/suites/nereids_rules_p0/defer_materialize_topn/lazy_materialize_topn.groovy
@@ -0,0 +1,73 @@
+// 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.
+suite("lazy_materialize_topn") {
+    sql """
+        set enable_two_phase_read_opt = true
+    """
+
+    sql """
+    drop table if exists lazy_materialize_topn;
+    """
+
+    sql """
+        CREATE TABLE `lazy_materialize_topn` (
+          `c1` int NULL,
+          `c2` int NULL,
+          `c3` int NULL,
+          `c4` array<int> NULL
+        )
+        PROPERTIES (
+          "replication_allocation" = "tag.location.default: 1",
+          "light_schema_change" = "true"
+        );
+    """
+
+    sql """
+        insert into lazy_materialize_topn values (1, 1, 1, [1]), (2, 2, 2, 
[2]), (3, 3, 3, [3]);
+    """
+
+    sql """
+        sync
+    """
+
+    List sqls = [
+            // TopN(Scan)
+            """select * from lazy_materialize_topn order by c1 limit 10""",
+            // TopN(Project(Scan))
+            """select c1, c2 from lazy_materialize_topn order by c1 limit 
10""",
+            // Project(TopN(Scan))
+            """select c1, c2, c3, c4 from lazy_materialize_topn order by c1 
limit 10""",
+            // Project(TopN(Project(Scan)))
+            """select c1 + 1, c2 + 1 from (select c1, c2 from 
lazy_materialize_topn order by c1 limit 10) t""",
+            // TopN(Filter(Scan))
+            """select * from lazy_materialize_topn where c2 < 5 order by c1 
limit 10;""",
+            // TopN(Project(Filter(Scan)))
+            """select c1, c2, c3 from lazy_materialize_topn where c2 < 5 order 
by c1 limit 10;""",
+            // Project(TopN(Project(Filter(Scan))))
+            """select c1 + 1, c2 + 1, c3 + 1 from ( select c1, c2, c3 from 
lazy_materialize_topn where c2 < 5 order by c1 limit 10) t""",
+            // project set is diff with output list
+            """select c1, c1, c2 from (select c1, c2 from 
lazy_materialize_topn where c3 < 1 order by c2 limit 1)t;"""
+    ]
+
+    for (sqlStr in sqls) {
+        explain {
+            sql """${sqlStr}"""
+            contains """OPT TWO PHASE"""
+        }
+        sql """${sqlStr}"""
+    }
+}
diff --git 
a/regression-test/suites/nereids_rules_p0/limit_push_down/order_push_down.groovy
 
b/regression-test/suites/nereids_rules_p0/limit_push_down/order_push_down.groovy
index 1ae008df588..bbf4a64f118 100644
--- 
a/regression-test/suites/nereids_rules_p0/limit_push_down/order_push_down.groovy
+++ 
b/regression-test/suites/nereids_rules_p0/limit_push_down/order_push_down.groovy
@@ -26,7 +26,7 @@ suite("order_push_down") {
     sql 'set be_number_for_test=3'
     sql "set disable_nereids_rules='push_down_top_n_distinct_through_union'"
     sql "set disable_nereids_rules=PRUNE_EMPTY_PARTITION"
-
+    sql 'set enable_two_phase_read_opt = true'
 
     //`limit 1 offset 1 + sort, project`:
     qt_limit_offset_sort_project """ explain shape plan SELECT t1.id FROM t1 
ORDER BY id LIMIT 1 OFFSET 1; """
diff --git 
a/regression-test/suites/nereids_rules_p0/mv/single_table_without_agg/single_table_without_aggregate.groovy
 
b/regression-test/suites/nereids_rules_p0/mv/single_table_without_agg/single_table_without_aggregate.groovy
index 450fb9c0ea3..3a07a959511 100644
--- 
a/regression-test/suites/nereids_rules_p0/mv/single_table_without_agg/single_table_without_aggregate.groovy
+++ 
b/regression-test/suites/nereids_rules_p0/mv/single_table_without_agg/single_table_without_aggregate.groovy
@@ -21,6 +21,9 @@ suite("single_table_without_aggregate") {
     sql "SET enable_nereids_planner=true"
     sql "SET enable_fallback_to_original_planner=false"
     sql "set enable_materialized_view_rewrite=true"
+    // TODO remove this variable after mv rewrite support defer materialized 
nodes
+    sql 'set enable_two_phase_read_opt = false'
+
 
     sql """
     drop table if exists orders
diff --git a/regression-test/suites/query_p0/sort/topn_2pr_rule.groovy 
b/regression-test/suites/query_p0/sort/topn_2pr_rule.groovy
index 4b74cdad853..0df8ab26030 100644
--- a/regression-test/suites/query_p0/sort/topn_2pr_rule.groovy
+++ b/regression-test/suites/query_p0/sort/topn_2pr_rule.groovy
@@ -52,7 +52,7 @@ suite("topn_2pr_rule") {
         } else if("${key_type}" == "UNIQUE") {
              explain {
                 sql("select * from ${table_name}  order by k limit 1;")
-                notContains "OPT TWO PHASE"
+                 contains "OPT TWO PHASE"
             } 
         } else if("${key_type}" == "AGGREGATE") {
              explain {


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

Reply via email to