This is an automated email from the ASF dual-hosted git repository.

englefly pushed a commit to branch lazy-mat-nest
in repository https://gitbox.apache.org/repos/asf/doris.git

commit a6f9d551df902409311d288efa0a56d6cbb1ac39
Author: englefly <[email protected]>
AuthorDate: Thu May 28 14:38:23 2026 +0800

    [fix](fe) preserve user-visible column order in TopN lazy mat final 
projection
    
    The final PhysicalProject above Materialize appended all pulled-up
    expressions at the end regardless of their original position in
    userVisibleOutput. When a nested expression (struct_element,
    element_at, etc.) was not the last SELECT column, the result
    schema was swapped.
    
    Fix by building outputExprs in userVisibleOutput order, replacing
    each pulled-up slot in-place with its corresponding expression
    via pulledUpExprMap, so output columns match the SQL SELECT order.
    
    Add regression tests (struct/variant/map nested expr before id).
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
---
 .../post/materialize/LazyMaterializeTopN.java      | 10 +++-
 .../topn_lazy_nested_column_pruning.groovy         | 61 ++++++++++++++++++++++
 2 files changed, 69 insertions(+), 2 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java
index 2d70bd9cdf2..b1cf86793bd 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java
@@ -145,13 +145,19 @@ public class LazyMaterializeTopN extends 
PlanPostProcessor {
             if (pulledUpExprs.isEmpty()) {
                 result = new 
PhysicalProject(ImmutableList.copyOf(userVisibleOutput), null, result);
             } else {
+                Map<ExprId, NamedExpression> pulledUpExprMap = new HashMap<>();
+                for (NamedExpression ne : pulledUpExprs) {
+                    pulledUpExprMap.put(ne.getExprId(), ne);
+                }
                 List<NamedExpression> outputExprs = new ArrayList<>();
                 for (Slot slot : userVisibleOutput) {
-                    if (!pulledUpExprIds.contains(slot.getExprId())) {
+                    NamedExpression pulledUpExpr = 
pulledUpExprMap.get(slot.getExprId());
+                    if (pulledUpExpr != null) {
+                        outputExprs.add(pulledUpExpr);
+                    } else {
                         outputExprs.add(slot);
                     }
                 }
-                outputExprs.addAll(pulledUpExprs);
                 result = new 
PhysicalProject(ImmutableList.copyOf(outputExprs), null, result);
             }
             return result;
diff --git 
a/regression-test/suites/nereids_rules_p0/column_pruning/topn_lazy_nested_column_pruning.groovy
 
b/regression-test/suites/nereids_rules_p0/column_pruning/topn_lazy_nested_column_pruning.groovy
index c0141215758..6246fc7819c 100644
--- 
a/regression-test/suites/nereids_rules_p0/column_pruning/topn_lazy_nested_column_pruning.groovy
+++ 
b/regression-test/suites/nereids_rules_p0/column_pruning/topn_lazy_nested_column_pruning.groovy
@@ -278,4 +278,65 @@ suite("topn_lazy_nested_column_pruning") {
         limit 3
     """
     sql """ set topn_lazy_materialization_using_index = false; """
+
+    // =============================================
+    // Test 15: STRUCT nested expr BEFORE id — verify column order preserved
+    //   SELECT city_expr, id should produce [city, id] not [id, city]
+    // =============================================
+    explain {
+        sql """
+            select substring(struct_element(struct_col, 'city'), 1) as city, id
+            from ncp_tbl
+            order by id
+            limit 3
+        """
+        contains("VMaterializeNode")
+        contains("row_ids: [__DORIS_GLOBAL_ROWID_COL__ncp_tbl]")
+    }
+    qt_struct_col_order_result """
+        select substring(struct_element(struct_col, 'city'), 1) as city, id
+        from ncp_tbl
+        order by id
+        limit 3
+    """
+
+    // =============================================
+    // Test 16: VARIANT nested expr BEFORE id — verify column order preserved
+    // =============================================
+    explain {
+        sql """
+            select substring(element_at(payload, 'name'), 1) as name, id, s
+            from vt
+            order by id
+            limit 3
+        """
+        contains("VMaterializeNode")
+        contains("row_ids: [__DORIS_GLOBAL_ROWID_COL__vt]")
+    }
+    qt_variant_col_order_result """
+        select substring(element_at(payload, 'name'), 1) as name, id, s
+        from vt
+        order by id
+        limit 3
+    """
+
+    // =============================================
+    // Test 17: MAP nested expr BEFORE id — verify column order preserved
+    // =============================================
+    explain {
+        sql """
+            select element_at(map_col, 'a') as val, id
+            from ncp_tbl
+            order by id
+            limit 3
+        """
+        contains("VMaterializeNode")
+        contains("row_ids: [__DORIS_GLOBAL_ROWID_COL__ncp_tbl]")
+    }
+    qt_map_col_order_result """
+        select element_at(map_col, 'a') as val, id
+        from ncp_tbl
+        order by id
+        limit 3
+    """
 }


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

Reply via email to