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 8ea383a7f19 [fix](rbo) Preserve union constant rows across rewrites
(#67790)
8ea383a7f19 is described below
commit 8ea383a7f19c1c3b0d76bad6ef9fd125145c363f
Author: morrySnow <[email protected]>
AuthorDate: Tue Sep 15 11:04:40 2026 +0800
[fix](rbo) Preserve union constant rows across rewrites (#67790)
## Problem
A UNION ALL could lose constant rows when a later rewrite exposed
another one-row child. Queries that combine an existing constant branch
with a single-row GROUP BY or DISTINCT branch returned only the newly
exposed row.
## Root cause
MergeOneRowRelationIntoUnion rebuilt the union constant-expression list
from only the current children. If an earlier rewrite pass had already
moved a constant child into the union metadata, a later pass replaced
that metadata instead of retaining it.
## Reproduction
```sql
SELECT 1 AS c
UNION ALL
SELECT c FROM (SELECT 2 AS c) s GROUP BY c;
```
The result incorrectly omitted the row containing 1. The same behavior
occurred with DISTINCT and with the branches reversed.
## Fix
Seed the rebuilt constant-expression list with the union existing
constant rows before appending newly converted one-row children. This
preserves row multiplicity across repeated rewrite passes.
Add a focused rewrite unit test and named regression queries with
runner-generated golden output for GROUP BY, DISTINCT, reversed branch
order, and duplicate constant rows.
## Tests
- `SetOperationOutputMappingTest`: 4 tests passed
- `merge_one_row_relation_into_union`: named golden regression passed in
both force-generation and normal comparison modes
- Full FE build with UI disabled: passed
- Checkstyle: passed
---
.../rewrite/MergeOneRowRelationIntoUnion.java | 1 +
.../rewrite/SetOperationOutputMappingTest.java | 44 ++++++++++++++++++++++
.../merge_one_row_relation_into_union.out | 18 +++++++++
.../merge_one_row_relation_into_union.groovy | 25 ++++++++++++
4 files changed, 88 insertions(+)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeOneRowRelationIntoUnion.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeOneRowRelationIntoUnion.java
index 7208aeba6e9..9b8052534cb 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeOneRowRelationIntoUnion.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeOneRowRelationIntoUnion.java
@@ -43,6 +43,7 @@ public class MergeOneRowRelationIntoUnion extends
OneRewriteRuleFactory {
return logicalUnion().when(u -> u.children().stream()
.anyMatch(LogicalOneRowRelation.class::isInstance)).then(u -> {
ImmutableList.Builder<List<NamedExpression>>
constantExprsList = ImmutableList.builder();
+ constantExprsList.addAll(u.getConstantExprsList());
List<Plan> newChildren = Lists.newArrayList();
ImmutableList.Builder<List<SlotReference>>
newChildrenOutputs = ImmutableList.builder();
for (int i = 0; i < u.arity(); i++) {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/SetOperationOutputMappingTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/SetOperationOutputMappingTest.java
index be2abce9128..a42c974dabf 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/SetOperationOutputMappingTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/SetOperationOutputMappingTest.java
@@ -96,6 +96,50 @@ class SetOperationOutputMappingTest extends
TestWithFeService implements MemoPat
Assertions.assertEquals(20, ((IntegerLiteral)
constantExprs.get(0).child(0)).getValue());
}
+ @Test
+ void testMergeOneRowRelationPreservesExistingConstantRows() {
+ Alias newConstant = new Alias(new ExprId(1), new IntegerLiteral(2),
"c");
+ LogicalOneRowRelation oneRowRelation = new LogicalOneRowRelation(
+ new RelationId(1), ImmutableList.of(newConstant));
+
+ SlotReference unionOutput = new SlotReference(new ExprId(10), "c",
+ IntegerType.INSTANCE, false, ImmutableList.of());
+ Alias firstExistingConstant = new Alias(new ExprId(20), new
IntegerLiteral(1), "c");
+ Alias secondExistingConstant = new Alias(new ExprId(21), new
IntegerLiteral(1), "c");
+ LogicalUnion union = new LogicalUnion(Qualifier.ALL,
+ ImmutableList.of(unionOutput),
+ ImmutableList.of(ImmutableList.of((SlotReference)
newConstant.toSlot())),
+ ImmutableList.of(
+ ImmutableList.of(firstExistingConstant),
+ ImmutableList.of(secondExistingConstant)),
+ false,
+ ImmutableList.of(oneRowRelation));
+
+ Plan rewritten =
PlanChecker.from(MemoTestUtils.createConnectContext(), union)
+ .applyTopDown(new MergeOneRowRelationIntoUnion())
+ .getPlan();
+
+ Assertions.assertInstanceOf(LogicalUnion.class, rewritten);
+ LogicalUnion rewrittenUnion = (LogicalUnion) rewritten;
+ Assertions.assertEquals(0, rewrittenUnion.children().size());
+ Assertions.assertEquals(3,
rewrittenUnion.getConstantExprsList().size());
+ Assertions.assertSame(firstExistingConstant,
+ rewrittenUnion.getConstantExprsList().get(0).get(0));
+ Assertions.assertSame(secondExistingConstant,
+ rewrittenUnion.getConstantExprsList().get(1).get(0));
+ Assertions.assertSame(newConstant,
+ rewrittenUnion.getConstantExprsList().get(2).get(0));
+
+ Plan rewrittenAgain =
PlanChecker.from(MemoTestUtils.createConnectContext(), rewrittenUnion)
+ .applyTopDown(new MergeOneRowRelationIntoUnion())
+ .getPlan();
+ Assertions.assertInstanceOf(LogicalUnion.class, rewrittenAgain);
+ LogicalUnion rewrittenAgainUnion = (LogicalUnion) rewrittenAgain;
+ Assertions.assertEquals(0, rewrittenAgainUnion.children().size());
+ Assertions.assertEquals(rewrittenUnion.getConstantExprsList(),
+ rewrittenAgainUnion.getConstantExprsList());
+ }
+
@Test
void testPushDownTopNDistinctThroughUnionUsesRegularChildOutput() {
String sql = "SELECT *\n"
diff --git
a/regression-test/data/nereids_rules_p0/merge_one_row_relation/merge_one_row_relation_into_union.out
b/regression-test/data/nereids_rules_p0/merge_one_row_relation/merge_one_row_relation_into_union.out
new file mode 100644
index 00000000000..76b69b05402
--- /dev/null
+++
b/regression-test/data/nereids_rules_p0/merge_one_row_relation/merge_one_row_relation_into_union.out
@@ -0,0 +1,18 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !preserve_constant_before_group_by --
+1
+2
+
+-- !preserve_constant_before_distinct --
+1
+2
+
+-- !preserve_constant_after_group_by --
+1
+2
+
+-- !preserve_multiple_constant_rows --
+1
+1
+1
+
diff --git
a/regression-test/suites/nereids_rules_p0/merge_one_row_relation/merge_one_row_relation_into_union.groovy
b/regression-test/suites/nereids_rules_p0/merge_one_row_relation/merge_one_row_relation_into_union.groovy
index 6e865d07c7d..ba8b242698c 100644
---
a/regression-test/suites/nereids_rules_p0/merge_one_row_relation/merge_one_row_relation_into_union.groovy
+++
b/regression-test/suites/nereids_rules_p0/merge_one_row_relation/merge_one_row_relation_into_union.groovy
@@ -28,4 +28,29 @@ suite("merge_one_row_relation_into_union") {
) u
GROUP BY v
"""
+
+ qt_preserve_constant_before_group_by """
+ SELECT 1 AS c
+ UNION ALL
+ SELECT c FROM (SELECT 2 AS c) s GROUP BY c
+ ORDER BY c
+ """
+
+ qt_preserve_constant_before_distinct """
+ SELECT 1 AS c
+ UNION ALL
+ SELECT DISTINCT c FROM (SELECT 2 AS c) s
+ ORDER BY c
+ """
+
+ qt_preserve_constant_after_group_by """
+ SELECT c FROM (SELECT 2 AS c) s GROUP BY c
+ UNION ALL
+ SELECT 1 AS c
+ ORDER BY c
+ """
+
+ qt_preserve_multiple_constant_rows """
+ SELECT 1 AS c UNION ALL SELECT 1 AS c UNION ALL SELECT 1 AS c ORDER BY
c
+ """
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]