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 742defc0d00 [fix](varaint) Preserve variant sub-path order (#67803)
742defc0d00 is described below

commit 742defc0d004aacf617af55cf5721339f398d0f2
Author: morrySnow <[email protected]>
AuthorDate: Fri Sep 11 15:11:59 2026 +0800

    [fix](varaint) Preserve variant sub-path order (#67803)
    
    ### What problem does this PR solve?
    
    Problem Summary:
    
    When nested-column pruning was disabled, a multi-level VARIANT access
    could return different values from the regular and constant branches of
    a `UNION ALL`. For example, with `{"a":{"b":1},"b":{"a":2}}`, selecting
    `c['a']['b']` returned `1` from the table branch but `2` from the
    constant branch.
    
    The pruning context stores sub-paths in canonical root-to-leaf order,
    such as `[a, b]`. The UNION constant-expression rewrite and the project
    pushdown rewrite rebuilt nested `ElementAt` expressions by iterating
    those paths backwards, producing `c['b']['a']`.
    
    This change centralizes construction of nested `ElementAt` expressions
    and applies each canonical sub-path from root to leaf. It uses the same
    helper for UNION constant expressions and projected VARIANT expressions
    so extraction and reconstruction share one order contract.
    
    The regression test reproduces the UNION result mismatch with
    nested-column pruning disabled, covers the projected-expression path,
    and verifies the enabled-pruning control case.
    
    ### Release note
    
    Fix incorrect values for multi-level VARIANT sub-paths in UNION ALL
    constant branches.
    
    ### Check List (For Author)
    
    - Test:
      - Unit Test: `PruneNestedColumnTest` (62 tests)
      - Regression Test: `variant_p0/test_variant_sub_path_order`
      - Full FE build
    - Behavior changed: Yes. Multi-level VARIANT sub-paths now preserve SQL
    access order across rewritten branches.
    - Does this need documentation: No
---
 .../rules/rewrite/VariantSubPathPruning.java       | 20 +++---
 .../rules/rewrite/PruneNestedColumnTest.java       | 14 +++++
 .../variant_p0/test_variant_sub_path_order.out     | 11 ++++
 .../variant_p0/test_variant_sub_path_order.groovy  | 72 ++++++++++++++++++++++
 4 files changed, 108 insertions(+), 9 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/VariantSubPathPruning.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/VariantSubPathPruning.java
index 5b081ce2c24..cbbd44baefc 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/VariantSubPathPruning.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/VariantSubPathPruning.java
@@ -332,10 +332,7 @@ public class VariantSubPathPruning implements 
CustomRewriter {
                         } else {
                             pushDownExpr = constExpr;
                         }
-                        for (int sp = entry.getKey().size() - 1; sp >= 0; 
sp--) {
-                            VarcharLiteral path = new 
VarcharLiteral(entry.getKey().get(sp));
-                            pushDownExpr = new ElementAt(pushDownExpr, path);
-                        }
+                        pushDownExpr = constructElementAt(pushDownExpr, 
entry.getKey());
                         constExprs.get(j).add(new Alias(pushDownExpr));
 
                     }
@@ -610,11 +607,7 @@ public class VariantSubPathPruning implements 
CustomRewriter {
             Set<List<String>> subPaths = context.slotToSubPathsMap
                     .get((SlotReference) projection.toSlot());
             for (List<String> subPath : subPaths) {
-                Expression pushDownExpr = child;
-                for (int i = subPath.size() - 1; i >= 0; i--) {
-                    VarcharLiteral path = new VarcharLiteral(subPath.get(i));
-                    pushDownExpr = new ElementAt(pushDownExpr, path);
-                }
+                Expression pushDownExpr = constructElementAt(child, subPath);
                 Alias alias = new Alias(pushDownExpr);
                 newProjections.add(alias);
                 subPathToSlot.put(subPath, (SlotReference) alias.toSlot());
@@ -781,6 +774,15 @@ public class VariantSubPathPruning implements 
CustomRewriter {
         }
     }
 
+    /** Build nested ElementAt expressions from a canonical root-to-leaf 
sub-path. */
+    protected static Expression constructElementAt(Expression root, 
List<String> subPath) {
+        Expression result = root;
+        for (String path : subPath) {
+            result = new ElementAt(result, new VarcharLiteral(path));
+        }
+        return result;
+    }
+
     protected static Pair<SlotReference, List<String>> 
extractSlotToSubPathPair(ElementAt elementAt) {
         List<String> subPath = Lists.newArrayList();
         while (true) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
index 7e66abb5925..a3e82f92d0c 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
@@ -1153,6 +1153,20 @@ public class PruneNestedColumnTest extends 
TestWithFeService implements MemoPatt
                 );
     }
 
+    @Test
+    public void testVariantSubPathConstructionOrder() {
+        SlotReference root = new SlotReference("v", VariantType.INSTANCE);
+        List<String> subPath = ImmutableList.of("a", "b", "c");
+
+        Expression expression = VariantSubPathPruning.constructElementAt(root, 
subPath);
+
+        Assertions.assertInstanceOf(ElementAt.class, expression);
+        Pair<SlotReference, List<String>> extracted = 
VariantSubPathPruning.extractSlotToSubPathPair(
+                (ElementAt) expression);
+        Assertions.assertEquals(root, extracted.first);
+        Assertions.assertEquals(subPath, extracted.second);
+    }
+
     @Test
     public void testDataTypeAccessTree() {
         List<Pair<SlotReference, DataTypeAccessTree>> trees = 
getDataTypeAccessTrees(
diff --git a/regression-test/data/variant_p0/test_variant_sub_path_order.out 
b/regression-test/data/variant_p0/test_variant_sub_path_order.out
new file mode 100644
index 00000000000..94e3d6d4e25
--- /dev/null
+++ b/regression-test/data/variant_p0/test_variant_sub_path_order.out
@@ -0,0 +1,11 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !union_constant_sub_path --
+1      table   1
+2      constant        1
+
+-- !project_sub_path --
+1      1
+
+-- !union_constant_sub_path_with_nested_pruning --
+1      table   1
+2      constant        1
diff --git 
a/regression-test/suites/variant_p0/test_variant_sub_path_order.groovy 
b/regression-test/suites/variant_p0/test_variant_sub_path_order.groovy
new file mode 100644
index 00000000000..925c1f628c5
--- /dev/null
+++ b/regression-test/suites/variant_p0/test_variant_sub_path_order.groovy
@@ -0,0 +1,72 @@
+// 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_variant_sub_path_order", "p0") {
+    sql "DROP TABLE IF EXISTS variant_sub_path_order"
+    sql """
+        CREATE TABLE variant_sub_path_order (
+            id INT NOT NULL,
+            v VARIANT NULL
+        ) ENGINE = OLAP
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_num" = "1")
+    """
+    sql """
+        INSERT INTO variant_sub_path_order VALUES
+            (1, '{"a":{"b":1},"b":{"a":2}}')
+    """
+
+    sql "SET experimental_enable_prune_nested_column = false"
+
+    order_qt_union_constant_sub_path """
+        WITH u AS (
+            SELECT id, 'table' AS branch_name, v AS c
+            FROM variant_sub_path_order
+            UNION ALL
+            SELECT 2 AS id, 'constant' AS branch_name,
+                    CAST('{"a":{"b":1},"b":{"a":2}}' AS VARIANT) AS c
+        )
+        SELECT id, branch_name, CAST(c['a']['b'] AS INT) AS value
+        FROM u
+        ORDER BY id
+    """
+
+    order_qt_project_sub_path """
+        SELECT id, CAST(c['a']['b'] AS INT) AS value
+        FROM (
+            SELECT id, IF(id > 0, v, CAST('{}' AS VARIANT)) AS c
+            FROM variant_sub_path_order
+        ) projected
+        ORDER BY id
+    """
+
+    sql "SET experimental_enable_prune_nested_column = true"
+
+    order_qt_union_constant_sub_path_with_nested_pruning """
+        WITH u AS (
+            SELECT id, 'table' AS branch_name, v AS c
+            FROM variant_sub_path_order
+            UNION ALL
+            SELECT 2 AS id, 'constant' AS branch_name,
+                    CAST('{"a":{"b":1},"b":{"a":2}}' AS VARIANT) AS c
+        )
+        SELECT id, branch_name, CAST(c['a']['b'] AS INT) AS value
+        FROM u
+        ORDER BY id
+    """
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to