This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new 187b22fbb88 branch-4.2: [fix](rbo) Rewrite LogicalGenerate lateral
conjuncts together with generators #66803 (#70034)
187b22fbb88 is described below
commit 187b22fbb88a5faa1190c3b32feca9c950ba8b6e
Author: morrySnow <[email protected]>
AuthorDate: Tue Nov 3 17:12:37 2026 +0800
branch-4.2: [fix](rbo) Rewrite LogicalGenerate lateral conjuncts together
with generators #66803 (#70034)
picked from master #66803
Co-authored-by: minghong <[email protected]>
---
.../rules/expression/ExpressionRewrite.java | 12 +++-
.../trees/plans/logical/LogicalGenerate.java | 16 +++++
.../rules/rewrite/GenerateConjunctRewriteTest.java | 79 ++++++++++++++++++++++
3 files changed, 105 insertions(+), 2 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionRewrite.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionRewrite.java
index e8a239f78a9..408bf03902f 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionRewrite.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionRewrite.java
@@ -129,10 +129,18 @@ public class ExpressionRewrite implements
RewriteRuleFactory {
List<Function> newGenerators = generators.stream()
.map(func -> (Function) rewriter.rewrite(func,
context))
.collect(ImmutableList.toImmutableList());
- if (generators.equals(newGenerators)) {
+ // lateral ON conjuncts must be rewritten together with the
generators:
+ // they reference the child output, so an ExprId replacement
(e.g. any_value
+ // wrapping in EliminateGroupByKeyByUniform) that is not
applied here would
+ // leave a stale slot and fail final slot validation.
+ List<Expression> conjuncts = generate.getConjuncts();
+ List<Expression> newConjuncts = conjuncts.stream()
+ .map(conjunct -> rewriter.rewrite(conjunct, context))
+ .collect(ImmutableList.toImmutableList());
+ if (generators.equals(newGenerators) &&
conjuncts.equals(newConjuncts)) {
return generate;
}
- return generate.withGenerators(newGenerators);
+ return generate.withGeneratorsAndConjuncts(newGenerators,
newConjuncts);
}).toRule(RuleType.REWRITE_GENERATE_EXPRESSION);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
index d5adde59537..2d9c27f5367 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
@@ -139,6 +139,22 @@ public class LogicalGenerate<CHILD_TYPE extends Plan>
extends LogicalUnary<CHILD
return new LogicalGenerate<>(generators, newGeneratorOutput,
expandColumnAlias, conjuncts, child());
}
+ /**
+ * update generators and lateral conjuncts in one operation, so that
expression rewrites
+ * (e.g. ExprId replacement) never leave a conjunct referencing a stale
slot from the
+ * previous child output.
+ */
+ public LogicalGenerate<Plan> withGeneratorsAndConjuncts(
+ List<Function> generators, List<Expression> conjuncts) {
+ Preconditions.checkArgument(generators.size() ==
generatorOutput.size());
+ List<Slot> newGeneratorOutput = Lists.newArrayList();
+ for (int i = 0; i < generators.size(); i++) {
+
newGeneratorOutput.add(generatorOutput.get(i).withNullable(generators.get(i).nullable()));
+ }
+ return new LogicalGenerate<>(generators, newGeneratorOutput,
expandColumnAlias,
+ Utils.fastToImmutableList(conjuncts), child());
+ }
+
@Override
public LogicalGenerate<Plan> withGroupExpression(Optional<GroupExpression>
groupExpression) {
return new LogicalGenerate<>(generators, generatorOutput,
expandColumnAlias, conjuncts,
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/GenerateConjunctRewriteTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/GenerateConjunctRewriteTest.java
new file mode 100644
index 00000000000..ebfe872385c
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/GenerateConjunctRewriteTest.java
@@ -0,0 +1,79 @@
+// 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.rewrite;
+
+import org.apache.doris.nereids.CascadesContext;
+import org.apache.doris.nereids.jobs.JobContext;
+import org.apache.doris.nereids.properties.PhysicalProperties;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.ExprId;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
+import org.apache.doris.nereids.trees.expressions.functions.generator.Unnest;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalGenerate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
+import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.VarcharType;
+import org.apache.doris.nereids.util.MemoTestUtils;
+import org.apache.doris.utframe.TestWithFeService;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+/**
+ * Tests that ExprId replacement (e.g. any_value wrapping in
EliminateGroupByKeyByUniform /
+ * EliminateGroupByKey) is also applied to LogicalGenerate lateral ON
conjuncts.
+ */
+class GenerateConjunctRewriteTest extends TestWithFeService {
+
+ @Test
+ void testExprIdRewriterRewritesLateralConjuncts() {
+ // Review P1: GenerateExpressionRewrite rewrote only getGenerators();
lateral ON
+ // conjuncts kept the stale ExprId after the child switched to wrapped
slots, so
+ // final slot validation rejected the query. The conjuncts must be
rewritten
+ // together with the generators.
+ SlotReference k = new SlotReference("k", IntegerType.INSTANCE);
+ SlotReference a = new SlotReference("a", IntegerType.INSTANCE);
+ SlotReference x = new SlotReference("x", VarcharType.SYSTEM_DEFAULT);
+ Unnest generator = new Unnest(new IntegerLiteral(1));
+ EqualTo conjunct = new EqualTo(k, a);
+ LogicalGenerate<Plan> generate = new LogicalGenerate<>(
+ ImmutableList.of(generator), ImmutableList.of(x),
ImmutableList.of(),
+ ImmutableList.of(conjunct), new LogicalOneRowRelation(
+ StatementScopeIdGenerator.newRelationId(),
ImmutableList.of(a)));
+
+ ExprId newK = new ExprId(999);
+ Map<ExprId, ExprId> replaceMap = ImmutableMap.of(k.getExprId(), newK);
+ CascadesContext cascadesContext =
MemoTestUtils.createCascadesContext(connectContext, generate);
+ ExprIdRewriter rewriter = new ExprIdRewriter(new
ExprIdRewriter.ReplaceRule(replaceMap, true),
+ new JobContext(cascadesContext, PhysicalProperties.ANY,
Double.MAX_VALUE));
+ LogicalGenerate<?> newGenerate = (LogicalGenerate<?>)
rewriter.rewriteExpr(generate, replaceMap);
+
+ Assertions.assertEquals(1, newGenerate.getConjuncts().size());
+ Slot newKSlot = (Slot) ((EqualTo)
newGenerate.getConjuncts().get(0)).child(0);
+ Assertions.assertEquals(newK, newKSlot.getExprId(),
+ "lateral ON conjunct must be rewritten with the new ExprId");
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]