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

JackieTien97 pushed a commit to branch codex/backport-pr-18179-dev-1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit cc6183b28445448dd0ba9a5cc9214c2816caea27
Author: JackieTien97 <[email protected]>
AuthorDate: Sat Jul 11 14:07:48 2026 +0800

    Fix query retry CI regressions
---
 .../db/queryengine/plan/analyze/TemplatedAggregationAnalyze.java | 9 +++++++--
 .../iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java    | 9 +++++++--
 .../db/queryengine/plan/analyze/TemplatedAnalyzeRetryTest.java   | 1 +
 3 files changed, 15 insertions(+), 4 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAggregationAnalyze.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAggregationAnalyze.java
index b7ac5792079..43c810ac808 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAggregationAnalyze.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAggregationAnalyze.java
@@ -181,12 +181,17 @@ public class TemplatedAggregationAnalyze {
         FunctionExpression countTimeExpression = (FunctionExpression) 
selectExpression;
         // The parsed count_time(*) expression belongs to the statement and 
may be analyzed again
         // after a dispatch failure. Build a template-only expression instead 
of replacing its
-        // wildcard child in place.
+        // wildcard child in place. Prime the copy's expression string before 
replacing the child:
+        // the result header must remain count_time(*), while the planning 
output symbol must be
+        // count_time(Time), just as it was when the parsed expression was 
mutated in place.
         FunctionExpression templatedCountTimeExpression =
             new FunctionExpression(
                 countTimeExpression.getFunctionName(),
                 new 
LinkedHashMap<>(countTimeExpression.getFunctionAttributes()),
-                Collections.singletonList(new TimestampOperand()));
+                new ArrayList<>(countTimeExpression.getExpressions()));
+        templatedCountTimeExpression.getExpressionString();
+        templatedCountTimeExpression.setExpressions(
+            Collections.singletonList(new TimestampOperand()));
         
templatedCountTimeExpression.setFunctionType(countTimeExpression.getFunctionType());
 
         outputExpressions.add(new Pair<>(templatedCountTimeExpression, 
resultColumn.getAlias()));
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
index 32d663c84e2..d348f2ba3d1 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
@@ -488,7 +488,8 @@ public class LogicalPlanBuilder {
 
     // DEVICE and TIME are implicit merge keys for DeviceView. Append them to 
a planning-owned copy
     // so rebuilding the plan after a dispatch failure does not mutate the 
parsed statement or
-    // accumulate duplicate keys.
+    // accumulate duplicate keys. Keep the complete parameter in Analysis 
because a downstream
+    // SortNode must use the same implicit keys without reading a mutated 
QueryStatement.
     List<SortItem> sortItemList = new 
ArrayList<>(queryStatement.getSortItemList());
     if (!queryStatement.isOrderByDevice()) {
       sortItemList.add(new SortItem(OrderByKey.DEVICE, Ordering.ASC));
@@ -498,6 +499,7 @@ public class LogicalPlanBuilder {
     }
 
     OrderByParameter orderByParameter = new OrderByParameter(sortItemList);
+    analysis.setMergeOrderParameter(orderByParameter);
 
     long limitValue =
         queryStatement.hasOffset()
@@ -1344,7 +1346,10 @@ public class LogicalPlanBuilder {
 
     Set<Expression> orderByExpressions = analysis.getOrderByExpressions();
     updateTypeProvider(orderByExpressions);
-    OrderByParameter orderByParameter = new 
OrderByParameter(queryStatement.getSortItemList());
+    OrderByParameter orderByParameter =
+        queryStatement.isAlignByDevice()
+            ? analysis.getMergeOrderParameter()
+            : new OrderByParameter(queryStatement.getSortItemList());
     if (orderByParameter.isEmpty()) {
       return this;
     }
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAnalyzeRetryTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAnalyzeRetryTest.java
index 9a45ac82fb1..773da476242 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAnalyzeRetryTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAnalyzeRetryTest.java
@@ -70,6 +70,7 @@ public class TemplatedAnalyzeRetryTest {
     FunctionExpression analyzedCountTimeExpression =
         (FunctionExpression) 
analysis.getAggregationExpressions().iterator().next();
     assertNotSame(countTimeExpression, analyzedCountTimeExpression);
+    assertEquals("count_time(*)", 
analyzedCountTimeExpression.getExpressionString());
     assertEquals("count_time(Time)", 
analyzedCountTimeExpression.getOutputSymbol());
 
     // QueryExecution analyzes the same statement again when retrying a 
dispatch failure.

Reply via email to