You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ca...@apache.org on 2023/08/02 16:49:57 UTC

[iotdb] branch beyyes/count_time_fe updated: fix transform

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

caogaofei pushed a commit to branch beyyes/count_time_fe
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/beyyes/count_time_fe by this push:
     new 6ad3d1131a7 fix transform
6ad3d1131a7 is described below

commit 6ad3d1131a7d8f0d40d1f5984049afae9e488280
Author: Beyyes <cg...@foxmail.com>
AuthorDate: Thu Aug 3 00:49:33 2023 +0800

    fix transform
---
 .../iotdb/db/it/aggregation/IoTDBCountTimeIT.java  |  33 +++
 .../queryengine/plan/analyze/AnalyzeVisitor.java   | 269 +++++++++++++--------
 .../plan/analyze/ExpressionTypeAnalyzer.java       |   4 +-
 .../plan/statement/component/ResultColumn.java     |   6 +-
 4 files changed, 213 insertions(+), 99 deletions(-)

diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBCountTimeIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBCountTimeIT.java
new file mode 100644
index 00000000000..adc6fc4d005
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBCountTimeIT.java
@@ -0,0 +1,33 @@
+package org.apache.iotdb.db.it.aggregation;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.prepareData;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBCountTimeIT {
+
+  protected static final String[] SQL_LIST = new String[] {"CREATE DATABASE root.db", "flush"};
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    // TODO set
+    EnvFactory.getEnv().getConfig().getCommonConfig().setPartitionInterval(1000);
+    EnvFactory.getEnv().initClusterEnvironment();
+    prepareData(SQL_LIST);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
index 48dc789b3ec..875adb5d198 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
@@ -183,6 +183,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.stream.Collectors;
@@ -196,6 +197,9 @@ import static org.apache.iotdb.db.queryengine.common.header.ColumnHeaderConstant
 import static org.apache.iotdb.db.queryengine.common.header.ColumnHeaderConstant.ENDTIME;
 import static org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet.PARTITION_FETCHER;
 import static org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet.SCHEMA_FETCHER;
+import static org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.concatDeviceAndBindSchemaForExpression;
+import static org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.getMeasurementExpression;
+import static org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.toLowerCaseExpression;
 import static org.apache.iotdb.db.queryengine.plan.analyze.SelectIntoUtils.constructTargetDevice;
 import static org.apache.iotdb.db.queryengine.plan.analyze.SelectIntoUtils.constructTargetMeasurement;
 import static org.apache.iotdb.db.queryengine.plan.analyze.SelectIntoUtils.constructTargetPath;
@@ -210,10 +214,10 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
 
   private static final IoTDBConfig CONFIG = IoTDBDescriptor.getInstance().getConfig();
 
-  private static final Expression deviceExpression =
+  private static final Expression DEVICE_EXPRESSION =
       TimeSeriesOperand.constructColumnHeaderExpression(DEVICE, TSDataType.TEXT);
 
-  private static final Expression endTimeExpression =
+  private static final Expression END_TIME_EXPRESSION =
       TimeSeriesOperand.constructColumnHeaderExpression(ENDTIME, TSDataType.INT64);
 
   private final List<String> lastQueryColumnNames =
@@ -306,7 +310,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
 
         Set<Expression> selectExpressions = new LinkedHashSet<>();
         if (queryStatement.isOutputEndTime()) {
-          selectExpressions.add(endTimeExpression);
+          selectExpressions.add(END_TIME_EXPRESSION);
         }
         for (Pair<Expression, String> outputExpressionAndAlias : outputExpressions) {
           selectExpressions.add(outputExpressionAndAlias.left);
@@ -503,6 +507,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
     }
   }
 
+  /** process select component for align by time */
   private Map<Integer, List<Pair<Expression, String>>> analyzeSelect(
       Analysis analysis, QueryStatement queryStatement, ISchemaTree schemaTree) {
     Map<Integer, List<Pair<Expression, String>>> outputExpressionMap = new HashMap<>();
@@ -523,9 +528,10 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
 
       if (AGGREGATION.equals(resultColumn.getColumnType())
           && (resultColumn.getExpression() instanceof FunctionExpression)
-          && (COUNT_TIME.equals(
+          && (COUNT_TIME.equalsIgnoreCase(
               ((FunctionExpression) resultColumn.getExpression()).getFunctionName()))) {
-        outputExpressions = analyzeCountTime(resultColumn, analysis, schemaTree, aliasSet);
+        outputExpressions =
+            analyzeCountTimeForAlignTime(resultColumn, analysis, schemaTree, aliasSet);
         outputExpressionMap.put(columnIndex++, outputExpressions);
         continue;
       }
@@ -563,12 +569,14 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
     return outputExpressionMap;
   }
 
-  private List<Pair<Expression, String>> analyzeCountTime(
+  /** process count_time aggregation query particularly */
+  private List<Pair<Expression, String>> analyzeCountTimeForAlignTime(
       ResultColumn resultColumn, Analysis analysis, ISchemaTree schemaTree, Set<String> aliasSet) {
-
+    // TODO limit, offset may have error?
     Set<Expression> sourceExpression = analysis.getSourceExpressions();
     if (sourceExpression == null) {
       sourceExpression = new HashSet<>();
+      analysis.setSourceExpressions(sourceExpression);
     }
 
     List<Expression> resultExpressions =
@@ -584,12 +592,11 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
         Set<Expression> sourceTransformExpressions = analysis.getSourceTransformExpressions();
         if (sourceTransformExpressions == null) {
           sourceTransformExpressions = new HashSet<>();
+          analysis.setSourceTransformExpressions(sourceTransformExpressions);
         }
         sourceTransformExpressions.add(normalizedExpression);
-        analysis.setSourceTransformExpressions(sourceTransformExpressions);
       }
     }
-    analysis.setSourceExpressions(sourceExpression);
 
     Expression expression =
         new FunctionExpression(
@@ -608,6 +615,52 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
     return outputExpressions;
   }
 
+  private void analyzeCountTimeForAlignDevice(
+      PartialPath device,
+      Analysis analysis,
+      Map<Expression, Map<String, Expression>> measurementToDeviceSelectExpressionMap,
+      List<Expression> selectExpressionsOfOneDevice) {
+    Map<String, Set<Expression>> deviceToSourceExpressions =
+        analysis.getDeviceToSourceExpressions();
+    Map<String, Set<Expression>> deviceToSourceTransformExpressions =
+        analysis.getDeviceToSourceTransformExpressions();
+    if (deviceToSourceExpressions == null) {
+      deviceToSourceExpressions = new HashMap<>();
+      analysis.setDeviceToSourceExpressions(deviceToSourceExpressions);
+    }
+    if (deviceToSourceTransformExpressions == null) {
+      deviceToSourceTransformExpressions = new HashMap<>();
+      analysis.setDeviceToSourceTransformExpressions(deviceToSourceTransformExpressions);
+    }
+
+    Expression countTimeExpression =
+        new FunctionExpression(
+            COUNT_TIME, new LinkedHashMap<>(), Collections.singletonList(new TimestampOperand()));
+    measurementToDeviceSelectExpressionMap
+        .computeIfAbsent(countTimeExpression, key -> new LinkedHashMap<>())
+        .put(device.getFullPath(), toLowerCaseExpression(countTimeExpression));
+
+    for (Expression expressions : selectExpressionsOfOneDevice) {
+      Expression expression = expressions.getExpressions().get(0);
+
+      analyzeExpressionType(analysis, expression);
+      if (expression instanceof TimeSeriesOperand) {
+        deviceToSourceExpressions
+            .computeIfAbsent(device.getFullPath(), key -> new LinkedHashSet<>())
+            .add(expression);
+      } else {
+        deviceToSourceTransformExpressions
+            .computeIfAbsent(device.getFullPath(), key -> new LinkedHashSet<>())
+            .add(expression);
+      }
+      Expression normalizedExpression = ExpressionAnalyzer.normalizeExpression(expression);
+    }
+
+    Map<NodeRef<Expression>, TSDataType> dataTypeMap = new HashMap<>();
+    dataTypeMap.put(new NodeRef<>(countTimeExpression), TSDataType.INT64);
+    analysis.addTypes(dataTypeMap);
+  }
+
   private Set<PartialPath> analyzeFrom(QueryStatement queryStatement, ISchemaTree schemaTree) {
     // device path patterns in FROM clause
     List<PartialPath> devicePatternList = queryStatement.getFromComponent().getPrefixPaths();
@@ -623,14 +676,15 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
     return deviceSet;
   }
 
+  /** process select component for align by device */
   private List<Pair<Expression, String>> analyzeSelect(
       Analysis analysis,
       QueryStatement queryStatement,
       ISchemaTree schemaTree,
       Set<PartialPath> deviceSet) {
-    List<Pair<Expression, String>> outputExpressions = new ArrayList<>();
-    Map<String, Set<Expression>> deviceToSelectExpressions = new HashMap<>();
 
+    List<Pair<Expression, String>> outputExpressions = new ArrayList<>();
+    Map<String, Set<Expression>> deviceToSelectExpressionsForAnalysis = new HashMap<>();
     ColumnPaginationController paginationController =
         new ColumnPaginationController(
             queryStatement.getSeriesLimit(), queryStatement.getSeriesOffset(), false);
@@ -641,27 +695,49 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
 
       // select expression after removing wildcard
       // use LinkedHashMap for order-preserving
-      Map<Expression, Map<String, Expression>> measurementToDeviceSelectExpressions =
+      Map<Expression, Map<String, Expression>> measurementToDeviceSelectExpressionMap =
           new LinkedHashMap<>();
       for (PartialPath device : deviceSet) {
         List<Expression> selectExpressionsOfOneDevice =
-            ExpressionAnalyzer.concatDeviceAndBindSchemaForExpression(
-                selectExpression, device, schemaTree);
+            concatDeviceAndBindSchemaForExpression(selectExpression, device, schemaTree);
         if (selectExpressionsOfOneDevice.isEmpty()) {
           continue;
         }
         noMeasurementDevices.remove(device);
-        updateMeasurementToDeviceSelectExpressions(
-            analysis, measurementToDeviceSelectExpressions, device, selectExpressionsOfOneDevice);
+
+        // process count_time aggregation query particularly
+        if (AGGREGATION.equals(resultColumn.getColumnType())
+            && (resultColumn.getExpression() instanceof FunctionExpression)
+            && (COUNT_TIME.equals(
+                ((FunctionExpression) resultColumn.getExpression()).getFunctionName()))) {
+
+          analyzeCountTimeForAlignDevice(
+              device,
+              analysis,
+              measurementToDeviceSelectExpressionMap,
+              selectExpressionsOfOneDevice);
+
+          // count_time query set count(X) as response header
+          // we use alias to represent count(X) here
+          if (resultColumn.getAlias() == null) {
+            resultColumn.setAlias(resultColumn.getExpression().toString());
+          }
+        } else {
+          for (Expression expression : selectExpressionsOfOneDevice) {
+            Expression measurementExpression = getMeasurementExpression(expression, analysis);
+            measurementToDeviceSelectExpressionMap
+                .computeIfAbsent(measurementExpression, key -> new LinkedHashMap<>())
+                .put(device.getFullPath(), toLowerCaseExpression(expression));
+          }
+        }
       }
 
-      checkAliasUniqueness(resultColumn.getAlias(), measurementToDeviceSelectExpressions);
+      checkAliasUniqueness(resultColumn.getAlias(), measurementToDeviceSelectExpressionMap);
 
-      for (Map.Entry<Expression, Map<String, Expression>> measurementDeviceSelectExpressionsEntry :
-          measurementToDeviceSelectExpressions.entrySet()) {
-        Expression measurementExpression = measurementDeviceSelectExpressionsEntry.getKey();
-        Map<String, Expression> deviceToSelectExpressionsOfOneMeasurement =
-            measurementDeviceSelectExpressionsEntry.getValue();
+      for (Map.Entry<Expression, Map<String, Expression>> entry :
+          measurementToDeviceSelectExpressionMap.entrySet()) {
+        Expression measurementExpression = entry.getKey();
+        Map<String, Expression> deviceToSelectExpressionsOfOneMeasurement = entry.getValue();
 
         if (paginationController.hasCurOffset()) {
           paginationController.consumeOffset();
@@ -675,20 +751,21 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
               analysis, new ArrayList<>(deviceToSelectExpressionsOfOneMeasurement.values()));
 
           // add outputExpressions
-          Expression normalizedMeasurementExpression =
-              ExpressionAnalyzer.toLowerCaseExpression(measurementExpression);
-          analyzeExpressionType(analysis, normalizedMeasurementExpression);
+          Expression lowerCaseMeasurementExpression = toLowerCaseExpression(measurementExpression);
+          analyzeExpressionType(analysis, lowerCaseMeasurementExpression);
           outputExpressions.add(
               new Pair<>(
-                  normalizedMeasurementExpression,
+                  lowerCaseMeasurementExpression,
                   analyzeAlias(
                       resultColumn.getAlias(),
                       measurementExpression,
-                      normalizedMeasurementExpression)));
+                      lowerCaseMeasurementExpression)));
 
           // add deviceToSelectExpressions
           updateDeviceToSelectExpressions(
-              analysis, deviceToSelectExpressions, deviceToSelectExpressionsOfOneMeasurement);
+              analysis,
+              deviceToSelectExpressionsForAnalysis,
+              deviceToSelectExpressionsOfOneMeasurement);
 
           paginationController.consumeLimit();
         } else {
@@ -707,51 +784,37 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
           devicePath -> analysis.getDeviceToWhereExpression().remove(devicePath.getFullPath()));
     }
 
-    analysis.setDeviceToSelectExpressions(deviceToSelectExpressions);
-
-    // set selectExpressions
+    // selectExpressions is the real series to read
+    // outputExpressions may contain alias or view
     Set<Expression> selectExpressions = new LinkedHashSet<>();
-    selectExpressions.add(deviceExpression);
+    selectExpressions.add(DEVICE_EXPRESSION);
     if (queryStatement.isOutputEndTime()) {
-      selectExpressions.add(endTimeExpression);
+      selectExpressions.add(END_TIME_EXPRESSION);
     }
-    selectExpressions.addAll(
-        outputExpressions.stream()
-            .map(Pair::getLeft)
-            .collect(Collectors.toCollection(LinkedHashSet::new)));
+    outputExpressions.forEach(pair -> selectExpressions.add(pair.getLeft()));
     analysis.setSelectExpressions(selectExpressions);
 
-    return outputExpressions;
-  }
+    analysis.setDeviceToSelectExpressions(deviceToSelectExpressionsForAnalysis);
 
-  private void updateMeasurementToDeviceSelectExpressions(
-      Analysis analysis,
-      Map<Expression, Map<String, Expression>> measurementToDeviceSelectExpressions,
-      PartialPath device,
-      List<Expression> selectExpressionsOfOneDevice) {
-    for (Expression expression : selectExpressionsOfOneDevice) {
-      Expression measurementExpression =
-          ExpressionAnalyzer.getMeasurementExpression(expression, analysis);
-      measurementToDeviceSelectExpressions
-          .computeIfAbsent(measurementExpression, key -> new LinkedHashMap<>())
-          .put(device.getFullPath(), ExpressionAnalyzer.toLowerCaseExpression(expression));
-    }
+    return outputExpressions;
   }
 
   private void updateDeviceToSelectExpressions(
       Analysis analysis,
-      Map<String, Set<Expression>> deviceToSelectExpressions,
+      Map<String, Set<Expression>> deviceToSelectExpressionsForAnalysis,
       Map<String, Expression> deviceToSelectExpressionsOfOneMeasurement) {
-    for (Map.Entry<String, Expression> deviceNameSelectExpressionEntry :
+
+    for (Map.Entry<String, Expression> entry :
         deviceToSelectExpressionsOfOneMeasurement.entrySet()) {
-      String deviceName = deviceNameSelectExpressionEntry.getKey();
-      Expression expression = deviceNameSelectExpressionEntry.getValue();
+      String deviceName = entry.getKey();
+      Expression expression = entry.getValue();
 
-      Expression normalizedExpression = ExpressionAnalyzer.toLowerCaseExpression(expression);
-      analyzeExpressionType(analysis, normalizedExpression);
-      deviceToSelectExpressions
+      Expression lowerCaseExpression = toLowerCaseExpression(expression);
+      // TODO may put duplicate expressions?
+      analyzeExpressionType(analysis, lowerCaseExpression);
+      deviceToSelectExpressionsForAnalysis
           .computeIfAbsent(deviceName, key -> new LinkedHashSet<>())
-          .add(normalizedExpression);
+          .add(lowerCaseExpression);
     }
   }
 
@@ -815,12 +878,11 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
 
     for (PartialPath device : deviceSet) {
       List<Expression> expressionsInHaving =
-          ExpressionAnalyzer.concatDeviceAndBindSchemaForExpression(
-              havingExpression, device, schemaTree);
+          concatDeviceAndBindSchemaForExpression(havingExpression, device, schemaTree);
 
       conJunctions.addAll(
           expressionsInHaving.stream()
-              .map(expression -> ExpressionAnalyzer.getMeasurementExpression(expression, analysis))
+              .map(expression -> getMeasurementExpression(expression, analysis))
               .collect(Collectors.toList()));
 
       for (Expression expression : expressionsInHaving) {
@@ -999,7 +1061,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
     for (Pair<Expression, String> outputExpressionAndAlias : outputExpressions) {
       FunctionExpression rawExpression = (FunctionExpression) outputExpressionAndAlias.getLeft();
       FunctionExpression measurementExpression =
-          (FunctionExpression) ExpressionAnalyzer.getMeasurementExpression(rawExpression, analysis);
+          (FunctionExpression) getMeasurementExpression(rawExpression, analysis);
       outputExpressionToRawExpressionsMap
           .computeIfAbsent(measurementExpression, v -> new HashSet<>())
           .add(rawExpression);
@@ -1116,29 +1178,38 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
 
   private void analyzeDeviceToSourceTransform(Analysis analysis, QueryStatement queryStatement) {
     if (queryStatement.isAggregationQuery()) {
-      Map<String, Set<Expression>> deviceToSourceTransformExpressions = new HashMap<>();
-
+      Map<String, Set<Expression>> deviceToSourceTransformExpressions =
+          analysis.getDeviceToSourceTransformExpressions();
       Map<String, Set<Expression>> deviceToAggregationExpressions =
           analysis.getDeviceToAggregationExpressions();
-      for (Map.Entry<String, Set<Expression>> deviceAggregationExpressionsEntry :
-          deviceToAggregationExpressions.entrySet()) {
-        String deviceName = deviceAggregationExpressionsEntry.getKey();
-        Set<Expression> aggregationExpressions = deviceAggregationExpressionsEntry.getValue();
+      for (Map.Entry<String, Set<Expression>> entry : deviceToAggregationExpressions.entrySet()) {
+        String deviceName = entry.getKey();
+        Set<Expression> aggregationExpressions = entry.getValue();
 
-        Set<Expression> sourceTransformExpressions = new LinkedHashSet<>();
+        // Set<Expression> sourceTransformExpressions = new LinkedHashSet<>();
         for (Expression expression : aggregationExpressions) {
+          // count_time aggregation not need transform
+          if (expression instanceof FunctionExpression
+              && COUNT_TIME.equalsIgnoreCase(((FunctionExpression) expression).getFunctionName())) {
+            continue;
+          }
+
           // We just process first input Expression of AggregationFunction,
           // keep other input Expressions as origin
           // If AggregationFunction need more than one input series,
           // we need to reconsider the process of it
-          sourceTransformExpressions.add(expression.getExpressions().get(0));
+          deviceToSourceTransformExpressions
+              .computeIfAbsent(deviceName, key -> new LinkedHashSet<>())
+              .add(expression.getExpressions().get(0));
+          // sourceTransformExpressions.add(expression.getExpressions().get(0));
         }
         if (queryStatement.hasGroupByExpression()) {
-          sourceTransformExpressions.add(analysis.getDeviceToGroupByExpression().get(deviceName));
+          deviceToSourceTransformExpressions
+              .computeIfAbsent(deviceName, key -> new LinkedHashSet<>())
+              .add(analysis.getDeviceToGroupByExpression().get(deviceName));
         }
-        deviceToSourceTransformExpressions.put(deviceName, sourceTransformExpressions);
+        // deviceToSourceTransformExpressions.put(deviceName, sourceTransformExpressions);
       }
-      analysis.setDeviceToSourceTransformExpressions(deviceToSourceTransformExpressions);
     } else {
       updateDeviceToSourceTransformAndOutputExpressions(
           analysis, analysis.getDeviceToSelectExpressions());
@@ -1179,9 +1250,16 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
   }
 
   private void analyzeSourceTransform(Analysis analysis, QueryStatement queryStatement) {
-    Set<Expression> sourceTransformExpressions = new HashSet<>();
+    Set<Expression> sourceTransformExpressions =
+        Optional.ofNullable(analysis.getSourceTransformExpressions()).orElse(new HashSet<>());
     if (queryStatement.isAggregationQuery()) {
       for (Expression expression : analysis.getAggregationExpressions()) {
+        // count_time aggregation not need transform
+        if (expression instanceof FunctionExpression
+            && COUNT_TIME.equalsIgnoreCase(((FunctionExpression) expression).getFunctionName())) {
+          continue;
+        }
+
         // for AggregationExpression, only the first Expression of input need to transform
         sourceTransformExpressions.add(expression.getExpressions().get(0));
       }
@@ -1198,20 +1276,24 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
   }
 
   private void analyzeDeviceToSource(Analysis analysis, QueryStatement queryStatement) {
-    Map<String, Set<Expression>> deviceToSourceExpressions = new HashMap<>();
+    Map<String, Set<Expression>> deviceToSourceExpressions =
+        Optional.ofNullable(analysis.getDeviceToSourceExpressions()).orElse(new HashMap<>());
     Map<String, Set<Expression>> deviceToSourceTransformExpressions =
         analysis.getDeviceToSourceTransformExpressions();
-    for (Map.Entry<String, Set<Expression>> deviceSourceTransformExpressionsEntry :
-        deviceToSourceTransformExpressions.entrySet()) {
-      String deviceName = deviceSourceTransformExpressionsEntry.getKey();
-      Set<Expression> sourceTransformExpressions = deviceSourceTransformExpressionsEntry.getValue();
+
+    for (Map.Entry<String, Set<Expression>> entry : deviceToSourceTransformExpressions.entrySet()) {
+      String deviceName = entry.getKey();
+      Set<Expression> sourceTransformExpressions = entry.getValue();
 
       Set<Expression> sourceExpressions = new LinkedHashSet<>();
       for (Expression expression : sourceTransformExpressions) {
         sourceExpressions.addAll(ExpressionAnalyzer.searchSourceExpressions(expression));
       }
-      deviceToSourceExpressions.put(deviceName, sourceExpressions);
+      deviceToSourceExpressions
+          .computeIfAbsent(deviceName, key -> new LinkedHashSet<>())
+          .addAll(sourceExpressions);
     }
+
     if (queryStatement.hasWhere()) {
       Map<String, Expression> deviceToWhereExpression = analysis.getDeviceToWhereExpression();
       for (Map.Entry<String, Expression> deviceWhereExpressionEntry :
@@ -1225,9 +1307,9 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
     }
 
     Map<String, List<String>> outputDeviceToQueriedDevicesMap = new LinkedHashMap<>();
-    for (Map.Entry<String, Set<Expression>> deviceSourceExpressionsEntry :
-        deviceToSourceExpressions.entrySet()) {
-      Set<Expression> sourceExpressionsUnderDevice = deviceSourceExpressionsEntry.getValue();
+    for (Map.Entry<String, Set<Expression>> entry : deviceToSourceExpressions.entrySet()) {
+      String deviceName = entry.getKey();
+      Set<Expression> sourceExpressionsUnderDevice = entry.getValue();
       Set<String> queriedDevices = new HashSet<>();
       for (Expression expression : sourceExpressionsUnderDevice) {
         queriedDevices.add(ExpressionAnalyzer.getDeviceNameInSourceExpression(expression));
@@ -1236,8 +1318,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
         throw new SemanticException(
             "Cross-device queries are not supported in ALIGN BY DEVICE queries.");
       }
-      outputDeviceToQueriedDevicesMap.put(
-          deviceSourceExpressionsEntry.getKey(), new ArrayList<>(queriedDevices));
+      outputDeviceToQueriedDevicesMap.put(deviceName, new ArrayList<>(queriedDevices));
     }
 
     analysis.setDeviceToSourceExpressions(deviceToSourceExpressions);
@@ -1334,9 +1415,9 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
     Set<Expression> selectExpressions = analysis.getSelectExpressions();
     Set<Expression> deviceViewOutputExpressions = new LinkedHashSet<>();
     if (queryStatement.isAggregationQuery()) {
-      deviceViewOutputExpressions.add(deviceExpression);
+      deviceViewOutputExpressions.add(DEVICE_EXPRESSION);
       if (queryStatement.isOutputEndTime()) {
-        deviceViewOutputExpressions.add(endTimeExpression);
+        deviceViewOutputExpressions.add(END_TIME_EXPRESSION);
       }
       for (Expression selectExpression : selectExpressions) {
         deviceViewOutputExpressions.addAll(
@@ -1400,8 +1481,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
         outputColumns.add(ENDTIME);
       }
       for (Expression expression : outputExpressionsUnderDevice) {
-        outputColumns.add(
-            ExpressionAnalyzer.getMeasurementExpression(expression, analysis).getOutputSymbol());
+        outputColumns.add(getMeasurementExpression(expression, analysis).getOutputSymbol());
       }
       deviceToOutputColumnsMap.put(deviceOutputExpressionEntry.getKey(), outputColumns);
     }
@@ -1535,8 +1615,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
       Expression expression = groupByComponent.getControlColumnExpression();
       for (PartialPath device : deviceSet) {
         List<Expression> groupByExpressionsOfOneDevice =
-            ExpressionAnalyzer.concatDeviceAndBindSchemaForExpression(
-                expression, device, schemaTree);
+            concatDeviceAndBindSchemaForExpression(expression, device, schemaTree);
 
         if (groupByExpressionsOfOneDevice.isEmpty()) {
           throw new SemanticException(
@@ -1609,8 +1688,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
       Set<Expression> orderByExpressionsForOneDevice = new LinkedHashSet<>();
       for (Expression expressionForItem : queryStatement.getExpressionSortItemList()) {
         List<Expression> expressions =
-            ExpressionAnalyzer.concatDeviceAndBindSchemaForExpression(
-                expressionForItem, device, schemaTree);
+            concatDeviceAndBindSchemaForExpression(expressionForItem, device, schemaTree);
         if (expressions.isEmpty()) {
           throw new SemanticException(
               String.format(
@@ -1629,8 +1707,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext>
               String.format("The data type of %s is not comparable", dataType));
         }
 
-        Expression deviceViewExpression =
-            ExpressionAnalyzer.getMeasurementExpression(expressionForItem, analysis);
+        Expression deviceViewExpression = getMeasurementExpression(expressionForItem, analysis);
         analyzeExpressionType(analysis, deviceViewExpression);
 
         deviceViewOrderByExpression.add(deviceViewExpression);
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionTypeAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionTypeAnalyzer.java
index e52fb95f481..41cd1fbe882 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionTypeAnalyzer.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionTypeAnalyzer.java
@@ -63,7 +63,7 @@ public class ExpressionTypeAnalyzer {
       ExpressionTypeAnalyzer analyzer = new ExpressionTypeAnalyzer();
       analyzer.analyze(expression);
 
-      updateAnalysis(analysis, analyzer);
+      addExpressionTypes(analysis, analyzer);
     }
     return analysis.getType(expression);
   }
@@ -76,7 +76,7 @@ public class ExpressionTypeAnalyzer {
     types.putAll(analyzer.getExpressionTypes());
   }
 
-  private static void updateAnalysis(Analysis analysis, ExpressionTypeAnalyzer analyzer) {
+  private static void addExpressionTypes(Analysis analysis, ExpressionTypeAnalyzer analyzer) {
     analysis.addTypes(analyzer.getExpressionTypes());
   }
 
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/component/ResultColumn.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/component/ResultColumn.java
index e8db3f8a5ee..02213ac2049 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/component/ResultColumn.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/component/ResultColumn.java
@@ -64,7 +64,7 @@ import java.util.Objects;
 public class ResultColumn extends StatementNode {
 
   private final Expression expression;
-  private final String alias;
+  private String alias;
 
   private final ColumnType columnType;
 
@@ -92,6 +92,10 @@ public class ResultColumn extends StatementNode {
     return alias;
   }
 
+  public void setAlias(String alias) {
+    this.alias = alias;
+  }
+
   public ColumnType getColumnType() {
     return columnType;
   }