This is an automated email from the ASF dual-hosted git repository.
morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 30d83e5c844 [fix](subquery) Preserve generate child outputs for
subqueries (#67807)
30d83e5c844 is described below
commit 30d83e5c8443dd78451a74e01717b0785fa9ad80
Author: morrySnow <[email protected]>
AuthorDate: Fri Sep 11 14:38:03 2026 +0800
[fix](subquery) Preserve generate child outputs for subqueries (#67807)
### What problem does this PR solve?
Problem Summary:
When a table-generating function contains a scalar subquery,
`NormalizeGenerate` inserts a `LogicalProject` to materialize the
subquery result. That project exposed only the new scalar-subquery
aliases and discarded every output from the original child.
Consequently, a projection above the `LATERAL VIEW` could no longer
resolve base-table columns. A generating expression that referenced both
a base-table column and the scalar subquery also lost its input slot.
For example, both of these valid forms failed analysis:
```sql
SELECT b.id, e
FROM base_table b
LATERAL VIEW explode_numbers((SELECT MAX(n) FROM base_table)) t AS e;
SELECT b.id, e
FROM base_table b
LATERAL VIEW explode_numbers(b.n + (SELECT MAX(n) FROM base_table)) t AS e;
```
### What is changed and how does it work?
The materialization project now retains the original child outputs, in
their existing order, before appending aliases for scalar-subquery
results. This preserves the child slots and their expression IDs for
both the generator and projections above it, while later projection
pruning can still remove unused internal slots.
Focused unit tests cover both dependencies: a base-table column consumed
above the generate node, and a base-table column consumed inside the
generating expression. A result regression covers the same two query
shapes end to end.
### Release note
Fix analysis failures for `LATERAL VIEW` generating functions that
combine scalar subqueries with base-table columns.
### Check List (For Author)
- Test:
- Unit Test: `NormalizeGenerateTest` (2 tests)
- Regression Test: `nereids_p0/test_generate_subquery_output`
- Full FE build and Checkstyle
- Behavior changed: Yes. Valid generating functions retain all required
child slots during scalar-subquery normalization.
- Does this need documentation: No
---
.../nereids/rules/analysis/NormalizeGenerate.java | 4 +-
.../rules/analysis/NormalizeGenerateTest.java | 51 ++++++++++++++++++++++
.../nereids_p0/test_generate_subquery_output.out | 15 +++++++
.../test_generate_subquery_output.groovy | 51 ++++++++++++++++++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeGenerate.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeGenerate.java
index 200dc04630c..4fba9a5eb89 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeGenerate.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/NormalizeGenerate.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
import org.apache.doris.nereids.trees.expressions.functions.Function;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
@@ -46,7 +47,8 @@ public class NormalizeGenerate extends OneAnalysisRuleFactory
{
List<Expression> subqueries =
ExpressionUtils.collectToList(
generate.getExpressions(),
SubqueryExpr.class::isInstance);
Map<Expression, Expression> replaceMap = new HashMap<>();
- ImmutableList.Builder<Alias> builder =
ImmutableList.builder();
+ ImmutableList.Builder<NamedExpression> builder =
ImmutableList.builder();
+ builder.addAll(generate.child().getOutput());
for (Expression expr : subqueries) {
Alias alias = new Alias(expr);
builder.add(alias);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/NormalizeGenerateTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/NormalizeGenerateTest.java
new file mode 100644
index 00000000000..f9f3ce70ffc
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/NormalizeGenerateTest.java
@@ -0,0 +1,51 @@
+// 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.analysis;
+
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class NormalizeGenerateTest extends TestWithFeService {
+ @Override
+ protected void runBeforeAll() throws Exception {
+ createDatabase("normalize_generate_test");
+ connectContext.setDatabase("normalize_generate_test");
+ createTable("CREATE TABLE base_table (id INT NOT NULL, n INT NOT NULL)
"
+ + "DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 1 "
+ + "PROPERTIES ('replication_num' = '1')");
+ }
+
+ @Test
+ public void testSubqueryKeepsGenerateChildOutput() {
+ assertAnalyzes("SELECT b.id, e FROM base_table b "
+ + "LATERAL VIEW explode_numbers((SELECT MAX(t2.n) FROM
base_table t2)) lv AS e");
+ }
+
+ @Test
+ public void testSubqueryKeepsGeneratorInput() {
+ assertAnalyzes("SELECT b.id, e FROM base_table b "
+ + "LATERAL VIEW explode_numbers(b.n + (SELECT MAX(t2.n) FROM
base_table t2)) lv AS e");
+ }
+
+ private void assertAnalyzes(String sql) {
+ Assertions.assertDoesNotThrow(() ->
PlanChecker.from(connectContext).analyze(sql).rewrite().getPlan(), sql);
+ }
+}
diff --git a/regression-test/data/nereids_p0/test_generate_subquery_output.out
b/regression-test/data/nereids_p0/test_generate_subquery_output.out
new file mode 100644
index 00000000000..5a302a3e287
--- /dev/null
+++ b/regression-test/data/nereids_p0/test_generate_subquery_output.out
@@ -0,0 +1,15 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !preserve_child_output --
+1 0
+1 1
+2 0
+2 1
+
+-- !preserve_generator_input --
+1 0
+1 1
+1 2
+2 0
+2 1
+2 2
+2 3
diff --git
a/regression-test/suites/nereids_p0/test_generate_subquery_output.groovy
b/regression-test/suites/nereids_p0/test_generate_subquery_output.groovy
new file mode 100644
index 00000000000..1a045627d34
--- /dev/null
+++ b/regression-test/suites/nereids_p0/test_generate_subquery_output.groovy
@@ -0,0 +1,51 @@
+// 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("test_generate_subquery_output", "p0") {
+ sql "SET enable_nereids_planner = true"
+ sql "SET enable_fallback_to_original_planner = false"
+
+ sql "DROP TABLE IF EXISTS generate_subquery_output"
+ sql """
+ CREATE TABLE generate_subquery_output (
+ id INT NOT NULL,
+ n INT NOT NULL
+ ) ENGINE = OLAP
+ DUPLICATE KEY(id)
+ DISTRIBUTED BY HASH(id) BUCKETS 1
+ PROPERTIES ("replication_num" = "1")
+ """
+ sql "INSERT INTO generate_subquery_output VALUES (1, 1), (2, 2)"
+
+ order_qt_preserve_child_output """
+ SELECT b.id, e
+ FROM generate_subquery_output b
+ LATERAL VIEW explode_numbers(
+ (SELECT MAX(t2.n) FROM generate_subquery_output t2)
+ ) lv AS e
+ ORDER BY b.id, e
+ """
+
+ order_qt_preserve_generator_input """
+ SELECT b.id, e
+ FROM generate_subquery_output b
+ LATERAL VIEW explode_numbers(
+ b.n + (SELECT MAX(t2.n) FROM generate_subquery_output t2)
+ ) lv AS e
+ ORDER BY b.id, e
+ """
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]