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]

Reply via email to