This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new d704934aafb branch-4.1 [feat](topn lazy materialize)using index topn
lazy (#59572) (#61309)
d704934aafb is described below
commit d704934aafbcb49fdd096a7fbb5b972752102c75
Author: minghong <[email protected]>
AuthorDate: Sat Mar 14 14:30:10 2026 +0800
branch-4.1 [feat](topn lazy materialize)using index topn lazy (#59572)
(#61309)
### What problem does this PR solve?
pick #59572
Issue Number: close #xxx
Related PR: #xxx
Problem Summary:
### Release note
None
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [ ] Regression test
- [ ] Unit Test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason <!-- Add your reason? -->
- Behavior changed:
- [ ] No.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [ ] No.
- [ ] Yes. <!-- Add document PR link here. eg:
https://github.com/apache/doris-website/pull/1214 -->
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->
---
.../doris/nereids/processor/post/Validator.java | 27 ++------
.../post/materialize/LazyMaterializeTopN.java | 80 ++++++++++++----------
.../post/materialize/LazySlotPruning.java | 51 +++++++++++++-
.../post/materialize/MaterializeProbeVisitor.java | 23 ++++++-
.../java/org/apache/doris/qe/SessionVariable.java | 13 ++++
.../topNLazyMaterializationUsingIndex.out | 43 ++++++++++++
.../topNLazyMaterializationUsingIndex.groovy | 77 +++++++++++++++++++++
7 files changed, 255 insertions(+), 59 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/Validator.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/Validator.java
index 40777e26faa..926e12cd279 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/Validator.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/Validator.java
@@ -32,11 +32,7 @@ import org.apache.doris.nereids.util.LazyCompute;
import com.google.common.base.Preconditions;
import java.util.BitSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
/**
* validator plan.
@@ -63,27 +59,20 @@ public class Validator extends PlanPostProcessor {
child.accept(this, context);
}
- Optional<Slot> opt = checkAllSlotFromChildren(plan);
- if (opt.isPresent()) {
- List<Slot> childrenOutput = plan.children().stream().flatMap(p ->
p.getOutput().stream()).collect(
- Collectors.toList());
- throw new AnalysisException("A expression contains slot not from
children\n"
- + "Slot: " + opt.get() + " Children Output:" +
childrenOutput + "\n"
- + "Plan: " + plan.treeString() + "\n");
- }
+ checkAllSlotFromChildren(plan);
return plan;
}
/**
* Check all slot must from children.
*/
- public static Optional<Slot> checkAllSlotFromChildren(Plan plan) {
+ public static void checkAllSlotFromChildren(Plan plan) {
if (plan.arity() == 0) {
- return Optional.empty();
+ return;
}
// agg exist multi-phase
if (plan instanceof Aggregate) {
- return Optional.empty();
+ return;
}
Supplier<BitSet> childrenOutputIds = LazyCompute.of(() -> {
@@ -97,7 +86,6 @@ public class Validator extends PlanPostProcessor {
});
for (Expression expression : plan.getExpressions()) {
- AtomicReference<Slot> invalidSlot = new AtomicReference<>();
expression.anyMatch(e -> {
if (e instanceof Slot) {
Slot slot = (Slot) e;
@@ -105,14 +93,13 @@ public class Validator extends PlanPostProcessor {
return false;
}
if
(!childrenOutputIds.get().get(slot.getExprId().asInt())) {
- invalidSlot.set(slot);
- return true;
+ throw new AnalysisException("A expression contains
slot not from children\n"
+ + "Slot: " + slot + " Children Output:" +
childrenOutputIds.get() + "\n"
+ + "Plan: " + plan.treeString() + "\n");
}
}
return false;
});
- return Optional.ofNullable(invalidSlot.get());
}
- return Optional.empty();
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java
index 3626c380b8a..6105b652445 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java
@@ -40,6 +40,8 @@ import org.apache.doris.qe.SessionVariable;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.HashMap;
@@ -53,17 +55,20 @@ import java.util.Set;
* post rule to do lazy materialize
*/
public class LazyMaterializeTopN extends PlanPostProcessor {
- /* BE do not support pattern:
- union
- -->materialize
- -->topn
- -->scan1
- -->materialize
- -->topn
- -->scan2
- when we create materializeNode for the first union child, set
hasMaterialized=true
- to avoid generating materializeNode for other union's children
- */
+ /*
+ * BE do not support pattern:
+ * union
+ * -->materialize
+ * -->topn
+ * -->scan1
+ * -->materialize
+ * -->topn
+ * -->scan2
+ * when we create materializeNode for the first union child, set
+ * hasMaterialized=true
+ * to avoid generating materializeNode for other union's children
+ */
+ private static final Logger LOG =
LogManager.getLogger(LazyMaterializeTopN.class);
private boolean hasMaterialized = false;
@Override
@@ -75,11 +80,11 @@ public class LazyMaterializeTopN extends PlanPostProcessor {
return topN;
}
/*
- topn(output=[x] orderkey=[b])
- ->project(a as x)
- ->T(a, b)
- 'x' can be lazy materialized.
- materializeMap: x->(T, a)
+ * topn(output=[x] orderkey=[b])
+ * ->project(a as x)
+ * ->T(a, b)
+ * 'x' can be lazy materialized.
+ * materializeMap: x->(T, a)
*/
Map<Slot, MaterializeSource> materializeMap = new HashMap<>();
List<Slot> materializedSlots = new ArrayList<>();
@@ -112,9 +117,12 @@ public class LazyMaterializeTopN extends PlanPostProcessor
{
List<Slot> originOutput = topN.getOutput();
BiMap<Relation, SlotReference> relationToRowId =
HashBiMap.create(relationToLazySlotMap.size());
HashSet<SlotReference> rowIdSet = new HashSet<>();
- // we should use threadStatementContext, not
ctx.getStatementContext(), because the StatisticsCleaner
- // will generate two statementContext, and reuse the plan which
generated by outer StatementContext,
- // so we should generate exprId by the outer StatementContext, or else
generate conflict expr id
+ // we should use threadStatementContext, not
ctx.getStatementContext(), because
+ // the StatisticsCleaner
+ // will generate two statementContext, and reuse the plan which
generated by
+ // outer StatementContext,
+ // so we should generate exprId by the outer StatementContext, or else
generate
+ // conflict expr id
StatementContext threadStatementContext =
StatementScopeIdGenerator.getStatementContext();
for (Relation relation : relationToLazySlotMap.keySet()) {
if (relation instanceof CatalogRelation) {
@@ -154,13 +162,13 @@ public class LazyMaterializeTopN extends
PlanPostProcessor {
if (materializeInput == null) {
/*
- topn
- -->any
- =>
- project
- -->materialize
- -->topn
- -->any
+ * topn
+ * -->any
+ * =>
+ * project
+ * -->materialize
+ * -->topn
+ * -->any
*/
result = new PhysicalLazyMaterialize(result, result.getOutput(),
materializedSlots, relationToLazySlotMap, relationToRowId,
materializeMap,
@@ -168,14 +176,14 @@ public class LazyMaterializeTopN extends
PlanPostProcessor {
hasMaterialized = true;
} else {
/*
- topn
- -->any
- =>
- project
- -->materialize
- -->project
- -->topn
- -->any
+ * topn
+ * -->any
+ * =>
+ * project
+ * -->materialize
+ * -->project
+ * -->topn
+ * -->any
*/
List<Slot> reOrderedMaterializedSlots = new ArrayList<>();
for (Slot slot : materializeInput) {
@@ -195,8 +203,8 @@ public class LazyMaterializeTopN extends PlanPostProcessor {
}
/*
- [a, r1, r2, b, r2] => [a, b, r1, r2]
- move all rowIds to tail, and remove duplicated rowIds
+ * [a, r1, r2, b, r2] => [a, b, r1, r2]
+ * move all rowIds to tail, and remove duplicated rowIds
*/
private List<SlotReference> moveRowIdsToTail(List<Slot> slots,
Set<SlotReference> rowIds) {
List<SlotReference> reArrangedSlots = new ArrayList<>();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazySlotPruning.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazySlotPruning.java
index bc54aa84bd2..c7b89519f1c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazySlotPruning.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazySlotPruning.java
@@ -27,6 +27,7 @@ import
org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalPlan;
import org.apache.doris.nereids.trees.plans.physical.PhysicalCTEConsumer;
import org.apache.doris.nereids.trees.plans.physical.PhysicalCTEProducer;
import org.apache.doris.nereids.trees.plans.physical.PhysicalFileScan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashAggregate;
import
org.apache.doris.nereids.trees.plans.physical.PhysicalLazyMaterializeFileScan;
import
org.apache.doris.nereids.trees.plans.physical.PhysicalLazyMaterializeOlapScan;
@@ -39,20 +40,24 @@ import
org.apache.doris.nereids.trees.plans.physical.PhysicalRepeat;
import org.apache.doris.nereids.trees.plans.physical.PhysicalSetOperation;
import org.apache.doris.nereids.trees.plans.physical.PhysicalTVFRelation;
import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
+import org.apache.doris.qe.SessionVariable;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
/**
- * prune lazy materialized slot
+ prune lazy materialized slot
*/
public class LazySlotPruning extends
DefaultPlanRewriter<LazySlotPruning.Context> {
/**
- * Context
+ Context
*/
public static class Context {
private PhysicalRelation scan;
@@ -98,6 +103,48 @@ public class LazySlotPruning extends
DefaultPlanRewriter<LazySlotPruning.Context
return plan;
}
+ @Override
+ public Plan visitPhysicalFilter(PhysicalFilter<? extends Plan> filter,
Context context) {
+ if (SessionVariable.getTopNLazyMaterializationUsingIndex() &&
filter.child() instanceof PhysicalOlapScan) {
+ /*
+ materialization(materializedSlots=[a, b], lazy=[c])
+ ->topn(b)
+ ->filter(a=1, output=(rowid, a, b))
+ ->materializeOlapScan(rowid, lazy=[c], T[a,b,c])
+ =>
+ materialization(materializedSlots=[b], lazy=[a, c])
+ ->topn(b)
+ ->project(rowid, b)
+ ->filter(a=1, output=(rowid, a, b))
+ ->materializeOlapScan(rowid, lazy=[a,c], T[a,b,c])
+ */
+ List<Slot> lazySlotsToScan = new ArrayList<>();
+ boolean lazySlotsChanged = false;
+
+ for (Slot slot : context.lazySlots) {
+ if (!filter.getInputSlots().contains(slot)) {
+ lazySlotsToScan.add(slot);
+ } else {
+ lazySlotsChanged = true;
+ }
+ }
+ if (lazySlotsChanged) {
+ Context contextForScan =
context.withLazySlots(lazySlotsToScan);
+ filter = (PhysicalFilter<? extends Plan>) filter.withChildren(
+ filter.child().accept(this, contextForScan));
+ filter = (PhysicalFilter<? extends Plan>) filter
+
.copyStatsAndGroupIdFrom(filter).resetLogicalProperties();
+ List<Slot> filterOutput =
Lists.newArrayList(filter.getOutput());
+ filterOutput.removeAll(filter.getInputSlots());
+ return new PhysicalProject<>(
+ filterOutput.stream().map(s -> (SlotReference)
s).collect(Collectors.toList()),
+ Optional.empty(), null,
+ filter.getPhysicalProperties(), filter.getStats(),
filter);
+ }
+ }
+ return visit(filter, context);
+ }
+
@Override
public Plan visitPhysicalOlapScan(PhysicalOlapScan scan, Context context) {
if (scan.getOutput().containsAll(context.lazySlots)) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/MaterializeProbeVisitor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/MaterializeProbeVisitor.java
index c6ff7709a7d..b6b4b31d8b6 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/MaterializeProbeVisitor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/MaterializeProbeVisitor.java
@@ -28,13 +28,16 @@ import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.algebra.Relation;
import org.apache.doris.nereids.trees.plans.physical.PhysicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
import org.apache.doris.nereids.trees.plans.physical.PhysicalLazyMaterialize;
import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
import org.apache.doris.nereids.trees.plans.physical.PhysicalSetOperation;
import org.apache.doris.nereids.trees.plans.physical.PhysicalTVFRelation;
import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
+import org.apache.doris.qe.SessionVariable;
import com.google.common.collect.ImmutableSet;
import org.apache.logging.log4j.LogManager;
@@ -71,6 +74,24 @@ public class MaterializeProbeVisitor extends
DefaultPlanVisitor<Optional<Materia
}
}
+ @Override
+ public Optional<MaterializeSource> visitPhysicalFilter(PhysicalFilter<?
extends Plan> filter,
+ ProbeContext
context) {
+ if (SessionVariable.getTopNLazyMaterializationUsingIndex() &&
filter.child() instanceof PhysicalOlapScan) {
+ // agg table do not support lazy materialize
+ OlapTable table = ((PhysicalOlapScan) filter.child()).getTable();
+ if (KeysType.AGG_KEYS.equals(table.getKeysType())) {
+ return Optional.empty();
+ }
+ if (filter.getInputSlots().contains(context.slot)) {
+ return Optional.of(new MaterializeSource((Relation)
filter.child(), context.slot));
+ } else {
+ return filter.child().accept(this, context);
+ }
+ }
+ return this.visit(filter, context);
+ }
+
@Override
public Optional<MaterializeSource> visit(Plan plan, ProbeContext context) {
if (plan.getInputSlots().contains(context.slot)) {
@@ -195,7 +216,7 @@ public class MaterializeProbeVisitor extends
DefaultPlanVisitor<Optional<Materia
} else {
// projectExpr is alias
Alias alias = (Alias) projectExpr;
- if (alias.child() instanceof SlotReference) {
+ if (alias.child() instanceof SlotReference &&
!SessionVariable.getTopNLazyMaterializationUsingIndex()) {
ProbeContext childContext = new ProbeContext((SlotReference)
alias.child());
return project.child().accept(this, childContext);
} else {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index db281d7cf1d..e105a839edd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -1629,6 +1629,11 @@ public class SessionVariable implements Serializable,
Writable {
varType = VariableAnnotation.EXPERIMENTAL)
public int topNLazyMaterializationThreshold = 1024;
+ @VariableMgr.VarAttr(name = "topn_lazy_materialization_using_index",
needForward = true,
+ fuzzy = false,
+ varType = VariableAnnotation.EXPERIMENTAL)
+ public boolean topNLazyMaterializationUsingIndex = false;
+
@VariableMgr.VarAttr(name = ENABLE_PRUNE_NESTED_COLUMN, needForward = true,
fuzzy = false,
varType = VariableAnnotation.EXPERIMENTAL,
@@ -1649,6 +1654,14 @@ public class SessionVariable implements Serializable,
Writable {
}
}
+ public static boolean getTopNLazyMaterializationUsingIndex() {
+ if (ConnectContext.get() != null) {
+ return
ConnectContext.get().getSessionVariable().topNLazyMaterializationUsingIndex;
+ } else {
+ return
VariableMgr.getDefaultSessionVariable().topNLazyMaterializationUsingIndex;
+ }
+ }
+
@VariableMgr.VarAttr(name = DISABLE_INVERTED_INDEX_V1_FOR_VARIANT,
needForward = true)
private boolean disableInvertedIndexV1ForVaraint = true;
diff --git
a/regression-test/data/query_p0/topn_lazy/usingIndex/topNLazyMaterializationUsingIndex.out
b/regression-test/data/query_p0/topn_lazy/usingIndex/topNLazyMaterializationUsingIndex.out
new file mode 100644
index 00000000000..d966e52f11e
--- /dev/null
+++
b/regression-test/data/query_p0/topn_lazy/usingIndex/topNLazyMaterializationUsingIndex.out
@@ -0,0 +1,43 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !plan --
+PhysicalResultSink
+--PhysicalProject[t1.addr, t1.age, t1.user_id, t1.username]
+----PhysicalLazyMaterialize[materializedSlots:(t1.username)
lazySlots:(t1.addr,t1.age,t1.user_id)]
+------PhysicalTopN[MERGE_SORT]
+--------PhysicalDistribute[DistributionSpecGather]
+----------PhysicalTopN[LOCAL_SORT]
+------------PhysicalProject[regression_test_query_p0_topn_lazy_usingIndex.__DORIS_GLOBAL_ROWID_COL__t1,
t1.username]
+--------------filter((t1.user_id = 1))
+----------------PhysicalLazyMaterializeOlapScan[t1 lazySlots:(t1.age,t1.addr)]
+
+-- !exec --
+1 a 10 cd
+
+-- !plan2 --
+PhysicalResultSink
+--PhysicalProject[t2.addr, t2.age, t2.user_id, t2.username]
+----PhysicalLazyMaterialize[materializedSlots:(t2.username)
lazySlots:(t2.addr,t2.age,t2.user_id)]
+------PhysicalTopN[MERGE_SORT]
+--------PhysicalDistribute[DistributionSpecGather]
+----------PhysicalTopN[LOCAL_SORT]
+------------PhysicalProject[regression_test_query_p0_topn_lazy_usingIndex.__DORIS_GLOBAL_ROWID_COL__t2,
t2.username]
+--------------hashJoin[INNER_JOIN broadcast] hashCondition=((t1.username =
t2.username)) otherCondition=() build RFs:RF0 username->[username];RF1
username->[username]
+----------------PhysicalProject[t1.username]
+------------------PhysicalOlapScan[t1] apply RFs: RF0 RF1
+----------------PhysicalProject[regression_test_query_p0_topn_lazy_usingIndex.__DORIS_GLOBAL_ROWID_COL__t2,
t2.username]
+------------------filter((t2.user_id > 0))
+--------------------PhysicalLazyMaterializeOlapScan[t2
lazySlots:(t2.age,t2.addr)]
+
+-- !exe2 --
+1 a 10 cd 1 a 10 cd
+
+-- !plan_no_effect --
+PhysicalResultSink
+--PhysicalProject[t1.addr, t1.age, t1.user_id, t1.username]
+----PhysicalLazyMaterialize[materializedSlots:(t1.user_id)
lazySlots:(t1.addr,t1.age,t1.username)]
+------PhysicalTopN[MERGE_SORT]
+--------PhysicalDistribute[DistributionSpecGather]
+----------PhysicalTopN[LOCAL_SORT]
+------------filter((t1.user_id > 0))
+--------------PhysicalLazyMaterializeOlapScan[t1
lazySlots:(t1.username,t1.age,t1.addr)]
+
diff --git
a/regression-test/suites/query_p0/topn_lazy/usingIndex/topNLazyMaterializationUsingIndex.groovy
b/regression-test/suites/query_p0/topn_lazy/usingIndex/topNLazyMaterializationUsingIndex.groovy
new file mode 100644
index 00000000000..8b839adfac7
--- /dev/null
+++
b/regression-test/suites/query_p0/topn_lazy/usingIndex/topNLazyMaterializationUsingIndex.groovy
@@ -0,0 +1,77 @@
+// 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("topNLazyMaterializationUsingIndex.groovy") {
+ sql """
+ drop table if exists t1;
+ CREATE TABLE t1
+ (
+ `user_id` LARGEINT NOT NULL,
+ `username` VARCHAR(50) NOT NULL,
+ age int,
+ addr VARCHAR(50) NOT NULL
+ )
+ duplicate KEY(user_id, username)
+ DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1");
+
+ insert into t1 values ( 1, 'a', 10, 'cd'),(1,'b', 20, 'cq');
+
+
+ drop table if exists t2;
+ CREATE TABLE t2
+ (
+ `user_id` LARGEINT NOT NULL,
+ `username` VARCHAR(50) NOT NULL,
+ age int,
+ addr VARCHAR(50) NOT NULL
+ )
+ duplicate KEY(user_id, username)
+ DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1");
+
+ insert into t2 values ( 1, 'a', 10, 'cd'),(1,'b', 20, 'cq');
+
+ set topn_lazy_materialization_using_index = true;
+ SET detail_shape_nodes='PhysicalProject';
+ """
+ qt_plan """
+ explain shape plan
+ select * from t1 where user_id = 1 order by username limit 1;
+ """
+ qt_exec """
+ select * from t1 where user_id = 1 order by username limit 1;
+ """
+
+ qt_plan2 """
+ explain shape plan
+ select t2.* from t1 join t2 on t1.username=t2.username where
t2.user_id > 0 order by username limit 1;
+ """
+
+ qt_exe2 """
+ select t2.*, t1.* from t1 join t2 on t1.username=t2.username where
t2.user_id > 0 order by username limit 1;
+ """
+
+ qt_plan_no_effect """
+ explain shape plan
+ select * from t1 where
+ user_id > 0 order by user_id limit 1;
+ """
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]