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

ericpai pushed a commit to branch feature/iotdb-4639
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit 04d71fc7f20b07967838e015b1fca84dadfb2eef
Author: ericpai <[email protected]>
AuthorDate: Wed Oct 19 15:59:12 2022 +0800

    Add hard mapping from output expression to result column
---
 .../db/it/aggregation/IoTDBTagAggregationIT.java   | 102 ++++++---
 .../apache/iotdb/db/mpp/plan/analyze/Analysis.java |  26 ++-
 .../iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java  | 249 +++++++++++----------
 .../db/mpp/plan/analyze/ConcatPathRewriter.java    |  43 ++--
 .../db/mpp/plan/planner/LogicalPlanBuilder.java    |  12 +-
 .../db/mpp/plan/statement/crud/QueryStatement.java |   2 +-
 6 files changed, 256 insertions(+), 178 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTagAggregationIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTagAggregationIT.java
index c481ca9718..ac906a3148 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTagAggregationIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTagAggregationIT.java
@@ -99,7 +99,7 @@ public class IoTDBTagAggregationIT {
     // Expected result set:
     // 
+----+--------+------------------+-----------+-----------+------------+------------+----------+
     // |  k1|count(t)|
-    // avg(t)|max_time(t)|min_time(t)|max_value(t)|min_value(t)|extreme(t)|
+    // AVG(t)|MAX_TIME(t)|MIN_TIME(t)|MAX_VALUE(t)|MIN_VALUE(t)|EXTREME(t)|
     // 
+----+--------+------------------+-----------+-----------+------------+------------+----------+
     // |k1v1|       6| 2.600000003973643|         10|          1|         6.5| 
        1.1|
     // 6.5|
@@ -123,13 +123,13 @@ public class IoTDBTagAggregationIT {
         Assert.assertEquals(6.5F, resultSet.getFloat(8), DELTA);
         Assert.assertTrue(resultSet.next());
         Assert.assertEquals("k1v2", resultSet.getString("k1"));
-        Assert.assertEquals(4L, resultSet.getLong("count(t)"));
-        Assert.assertEquals(3.1D, resultSet.getDouble("avg(t)"), DELTA);
-        Assert.assertEquals(10L, resultSet.getLong("max_time(t)"));
-        Assert.assertEquals(1L, resultSet.getLong("min_time(t)"));
-        Assert.assertEquals(5.4F, resultSet.getFloat("max_value(t)"), DELTA);
-        Assert.assertEquals(1.3F, resultSet.getFloat("min_value(t)"), DELTA);
-        Assert.assertEquals(5.4F, resultSet.getFloat("extreme(t)"), DELTA);
+        Assert.assertEquals(4L, resultSet.getLong("COUNT(t)"));
+        Assert.assertEquals(3.1D, resultSet.getDouble("AVG(t)"), DELTA);
+        Assert.assertEquals(10L, resultSet.getLong("MAX_TIME(t)"));
+        Assert.assertEquals(1L, resultSet.getLong("MIN_TIME(t)"));
+        Assert.assertEquals(5.4F, resultSet.getFloat("MAX_VALUE(t)"), DELTA);
+        Assert.assertEquals(1.3F, resultSet.getFloat("MIN_VALUE(t)"), DELTA);
+        Assert.assertEquals(5.4F, resultSet.getFloat("EXTREME(t)"), DELTA);
         Assert.assertTrue(resultSet.next());
         Assert.assertNull(resultSet.getString(1));
         Assert.assertEquals(4L, resultSet.getLong(2));
@@ -153,7 +153,7 @@ public class IoTDBTagAggregationIT {
     String query = "SELECT COUNT(t + 1), AVG(t + 1) FROM root.sg.** GROUP BY 
TAGS(k1)";
     // Expected result set:
     // +----+------------+------------------+
-    // |  k1|count(t + 1)|        avg(t + 1)|
+    // |  k1|COUNT(t + 1)|        AVG(t + 1)|
     // +----+------------+------------------+
     // |k1v1|           6| 3.600000003973643|
     // |k1v2|           4|3.1000000536441803|
@@ -205,8 +205,8 @@ public class IoTDBTagAggregationIT {
         "SELECT COUNT(t), AVG(t), MAX_TIME(t), MIN_TIME(t), MAX_VALUE(t), 
MIN_VALUE(t), EXTREME(t) FROM root.sg.** GROUP BY TAGS(k1) HAVING avg(t) > 3";
     // Expected result set:
     // 
+----+--------+------------------+-----------+-----------+------------+------------+----------+
-    // |  k1|count(t)|
-    // avg(t)|max_time(t)|min_time(t)|max_value(t)|min_value(t)|extreme(t)|
+    // |  k1|COUNT(t)|
+    // AVG(t)|MAX_TIME(t)|MIN_TIME(t)|MAX_VALUE(t)|MIN_VALUE(t)|EXTREME(t)|
     // 
+----+--------+------------------+-----------+-----------+------------+------------+----------+
     // |k1v2|       4|3.1000000536441803|         10|          1|         5.4| 
        1.3|
     // 5.4|
@@ -219,13 +219,13 @@ public class IoTDBTagAggregationIT {
         Assert.assertEquals(8, resultSet.getMetaData().getColumnCount());
         Assert.assertTrue(resultSet.next());
         Assert.assertEquals("k1v2", resultSet.getString("k1"));
-        Assert.assertEquals(4L, resultSet.getLong("count(t)"));
-        Assert.assertEquals(3.1D, resultSet.getDouble("avg(t)"), DELTA);
-        Assert.assertEquals(10L, resultSet.getLong("max_time(t)"));
-        Assert.assertEquals(1L, resultSet.getLong("min_time(t)"));
-        Assert.assertEquals(5.4F, resultSet.getFloat("max_value(t)"), DELTA);
-        Assert.assertEquals(1.3F, resultSet.getFloat("min_value(t)"), DELTA);
-        Assert.assertEquals(5.4F, resultSet.getFloat("extreme(t)"), DELTA);
+        Assert.assertEquals(4L, resultSet.getLong("COUNT(t)"));
+        Assert.assertEquals(3.1D, resultSet.getDouble("AVG(t)"), DELTA);
+        Assert.assertEquals(10L, resultSet.getLong("MAX_TIME(t)"));
+        Assert.assertEquals(1L, resultSet.getLong("MIN_TIME(t)"));
+        Assert.assertEquals(5.4F, resultSet.getFloat("MAX_VALUE(t)"), DELTA);
+        Assert.assertEquals(1.3F, resultSet.getFloat("MIN_VALUE(t)"), DELTA);
+        Assert.assertEquals(5.4F, resultSet.getFloat("EXTREME(t)"), DELTA);
         Assert.assertTrue(resultSet.next());
         Assert.assertNull(resultSet.getString(1));
         Assert.assertEquals(4L, resultSet.getLong(2));
@@ -248,7 +248,7 @@ public class IoTDBTagAggregationIT {
     String query = "SELECT COUNT(t) FROM root.sg.** GROUP BY TAGS(k1, k2)";
     // Expected result set:
     // +----+----+--------+
-    // |  k1|  k2|count(t)|
+    // |  k1|  k2|COUNT(t)|
     // +----+----+--------+
     // |k1v1|k2v1|       2|
     // |k1v1|k2v2|       2|
@@ -276,7 +276,7 @@ public class IoTDBTagAggregationIT {
           Assert.assertTrue(resultSet.next());
           Assert.assertEquals(objects[0], resultSet.getString("k1"));
           Assert.assertEquals(objects[1], resultSet.getString("k2"));
-          Assert.assertEquals(objects[2], resultSet.getLong("count(t)"));
+          Assert.assertEquals(objects[2], resultSet.getLong("COUNT(t)"));
         }
         Assert.assertFalse(resultSet.next());
       }
@@ -291,7 +291,49 @@ public class IoTDBTagAggregationIT {
     String query = "SELECT COUNT(t) from root.sg.** GROUP BY ([0, 20), 10ms), 
TAGS(k1)";
     // Expected result set:
     // +-----------------------------+----+--------+
-    // |                         Time|  k1|count(t)|
+    // |                         Time|  k1|COUNT(t)|
+    // +-----------------------------+----+--------+
+    // |1970-01-01T08:00:00.000+08:00|k1v1|       3|
+    // |1970-01-01T08:00:00.000+08:00|k1v2|       2|
+    // |1970-01-01T08:00:00.000+08:00|null|       2|
+    // |1970-01-01T08:00:00.010+08:00|k1v1|       3|
+    // |1970-01-01T08:00:00.010+08:00|k1v2|       2|
+    // |1970-01-01T08:00:00.010+08:00|null|       2|
+    // +-----------------------------+----+--------+
+    Object[][] expected =
+        new Object[][] {
+          {0L, "k1v1", 3L},
+          {0L, "k1v2", 2L},
+          {0L, null, 2L},
+          {10L, "k1v1", 3L},
+          {10L, "k1v2", 2L},
+          {10L, null, 2L},
+        };
+    try (Connection connection = EnvFactory.getEnv().getConnection();
+        Statement statement = connection.createStatement()) {
+      try (ResultSet resultSet = statement.executeQuery(query)) {
+        Assert.assertEquals(3, resultSet.getMetaData().getColumnCount());
+        for (Object[] objects : expected) {
+          Assert.assertTrue(resultSet.next());
+          Assert.assertEquals(objects[0], resultSet.getLong("Time"));
+          Assert.assertEquals(objects[1], resultSet.getString("k1"));
+          Assert.assertEquals(objects[2], resultSet.getLong("COUNT(t)"));
+        }
+        Assert.assertFalse(resultSet.next());
+      }
+    } catch (SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void testAlongWithTimeAggregationWithAlias() {
+    String query =
+        "SELECT COUNT(t), COUNT(t), COUNT(t) as a from root.sg.** GROUP BY 
([0, 20), 10ms), TAGS(k1)";
+    // Expected result set:
+    // +-----------------------------+----+--------+
+    // |                         Time|  k1|COUNT(t)|
     // +-----------------------------+----+--------+
     // |1970-01-01T08:00:00.000+08:00|k1v1|       3|
     // |1970-01-01T08:00:00.000+08:00|k1v2|       2|
@@ -317,7 +359,7 @@ public class IoTDBTagAggregationIT {
           Assert.assertTrue(resultSet.next());
           Assert.assertEquals(objects[0], resultSet.getLong("Time"));
           Assert.assertEquals(objects[1], resultSet.getString("k1"));
-          Assert.assertEquals(objects[2], resultSet.getLong("count(t)"));
+          Assert.assertEquals(objects[2], resultSet.getLong("COUNT(t)"));
         }
         Assert.assertFalse(resultSet.next());
       }
@@ -332,7 +374,7 @@ public class IoTDBTagAggregationIT {
     String query = "SELECT COUNT(t) from root.sg.** GROUP BY ([0, 20), 15ms, 
5ms), TAGS(k1)";
     // Expected result set:
     // +-----------------------------+----+--------+
-    // |                         Time|  k1|count(t)|
+    // |                         Time|  k1|COUNT(t)|
     // +-----------------------------+----+--------+
     // |1970-01-01T08:00:00.000+08:00|k1v1|       6|
     // |1970-01-01T08:00:00.000+08:00|k1v2|       4|
@@ -370,7 +412,7 @@ public class IoTDBTagAggregationIT {
           Assert.assertTrue(resultSet.next());
           Assert.assertEquals(objects[0], resultSet.getLong("Time"));
           Assert.assertEquals(objects[1], resultSet.getString("k1"));
-          Assert.assertEquals(objects[2], resultSet.getLong("count(t)"));
+          Assert.assertEquals(objects[2], resultSet.getLong("COUNT(t)"));
         }
         Assert.assertFalse(resultSet.next());
       }
@@ -386,7 +428,7 @@ public class IoTDBTagAggregationIT {
         "SELECT COUNT(t) from root.sg.** GROUP BY ([0, 20), 10ms), TAGS(k1) 
ORDER BY TIME DESC";
     // Expected result set:
     // +-----------------------------+----+--------+
-    // |                         Time|  k1|count(t)|
+    // |                         Time|  k1|COUNT(t)|
     // +-----------------------------+----+--------+
     // |1970-01-01T08:00:00.010+08:00|k1v1|       3|
     // |1970-01-01T08:00:00.010+08:00|k1v2|       2|
@@ -412,7 +454,7 @@ public class IoTDBTagAggregationIT {
           Assert.assertTrue(resultSet.next());
           Assert.assertEquals(objects[0], resultSet.getLong("Time"));
           Assert.assertEquals(objects[1], resultSet.getString("k1"));
-          Assert.assertEquals(objects[2], resultSet.getLong("count(t)"));
+          Assert.assertEquals(objects[2], resultSet.getLong("COUNT(t)"));
         }
         Assert.assertFalse(resultSet.next());
       }
@@ -427,7 +469,7 @@ public class IoTDBTagAggregationIT {
     String query = "SELECT COUNT(t) FROM root.sg.** WHERE time > 1 GROUP BY 
TAGS(k1)";
     // Expected result set:
     // +----+--------+
-    // |  k1|count(t)|
+    // |  k1|COUNT(t)|
     // +----+--------+
     // |k1v1|       3|
     // |k1v2|       2|
@@ -439,13 +481,13 @@ public class IoTDBTagAggregationIT {
         Assert.assertEquals(2, resultSet.getMetaData().getColumnCount());
         Assert.assertTrue(resultSet.next());
         Assert.assertEquals("k1v1", resultSet.getString("k1"));
-        Assert.assertEquals(3, resultSet.getLong("count(t)"));
+        Assert.assertEquals(3, resultSet.getLong("COUNT(t)"));
         Assert.assertTrue(resultSet.next());
         Assert.assertEquals("k1v2", resultSet.getString("k1"));
-        Assert.assertEquals(2, resultSet.getLong("count(t)"));
+        Assert.assertEquals(2, resultSet.getLong("COUNT(t)"));
         Assert.assertTrue(resultSet.next());
         Assert.assertNull(resultSet.getString("k1"));
-        Assert.assertEquals(2, resultSet.getLong("count(t)"));
+        Assert.assertEquals(2, resultSet.getLong("COUNT(t)"));
         Assert.assertFalse(resultSet.next());
       }
 
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java
index dec1ba3ba8..5df0de4175 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java
@@ -29,6 +29,7 @@ import org.apache.iotdb.db.mpp.common.NodeRef;
 import org.apache.iotdb.db.mpp.common.header.DatasetHeader;
 import org.apache.iotdb.db.mpp.common.schematree.ISchemaTree;
 import org.apache.iotdb.db.mpp.plan.expression.Expression;
+import org.apache.iotdb.db.mpp.plan.expression.ResultColumn;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.parameter.DeviceViewIntoPathDescriptor;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.FillDescriptor;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
@@ -39,6 +40,7 @@ import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
 import org.apache.iotdb.tsfile.utils.Pair;
 
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -74,6 +76,14 @@ public class Analysis {
   // fail.
   private String failMessage;
 
+  // As every result column will be analyzed with path concatenation and 
wildcard removing to
+  // generate one or more output expressions. We can maintain a map from 
output expression to
+  // original result column for later use.
+  // Note that if multiple expressions come from one single ResultColumn, the 
right object's
+  // REFERENCE should be equal, aka outputExpressions.get(i).right ==
+  // outputExpressions.get(j).right, NOT VALUE equal.
+  private List<Pair<Expression, ResultColumn>> outputExpressions = new 
ArrayList<>();
+
   
/////////////////////////////////////////////////////////////////////////////////////////////////
   // Query Analysis (used in ALIGN BY TIME)
   
/////////////////////////////////////////////////////////////////////////////////////////////////
@@ -96,7 +106,7 @@ public class Analysis {
   // tag keys specified in `GROUP BY TAG` clause
   private List<String> tagKeys;
 
-  // {tag values -> {grouped expression -> output expressions}}
+  // {tag values -> {grouped expression -> source timeseries expressions}}
   // For different combination of tag keys, the grouped expression may be 
different. Let's say there
   // are 3 timeseries root.sg.d1.temperature, root.sg.d1.status, 
root.sg.d2.temperature, and their
   // tags are [k1=v1], [k1=v1] and [k1=v2] respectively. For query "SELECT 
last_value(**) FROM root
@@ -105,7 +115,7 @@ public class Analysis {
   // Thus, the aggregation results of bucket [v1] and [v2] are different. 
Bucket [v1] has 2
   // aggregation results last_value(temperature) and last_value(status), 
whereas bucket [v2] only
   // has [last_value(temperature)].
-  private Map<List<String>, LinkedHashMap<Expression, List<Expression>>>
+  private Map<List<String>, LinkedHashMap<Expression, Set<Expression>>>
       tagValuesToGroupedTimeseriesOperands;
 
   
/////////////////////////////////////////////////////////////////////////////////////////////////
@@ -486,14 +496,22 @@ public class Analysis {
     this.tagKeys = tagKeys;
   }
 
-  public Map<List<String>, LinkedHashMap<Expression, List<Expression>>>
+  public Map<List<String>, LinkedHashMap<Expression, Set<Expression>>>
       getTagValuesToGroupedTimeseriesOperands() {
     return tagValuesToGroupedTimeseriesOperands;
   }
 
   public void setTagValuesToGroupedTimeseriesOperands(
-      Map<List<String>, LinkedHashMap<Expression, List<Expression>>>
+      Map<List<String>, LinkedHashMap<Expression, Set<Expression>>>
           tagValuesToGroupedTimeseriesOperands) {
     this.tagValuesToGroupedTimeseriesOperands = 
tagValuesToGroupedTimeseriesOperands;
   }
+
+  public List<Pair<Expression, ResultColumn>> getOutputExpressions() {
+    return outputExpressions;
+  }
+
+  public void setOutputExpressions(List<Pair<Expression, ResultColumn>> 
outputExpressions) {
+    this.outputExpressions = outputExpressions;
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java
index 6cb199c569..250ad76c79 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.commons.partition.DataPartition;
 import org.apache.iotdb.commons.partition.DataPartitionQueryParam;
 import org.apache.iotdb.commons.partition.SchemaNodeManagementPartition;
 import org.apache.iotdb.commons.partition.SchemaPartition;
+import org.apache.iotdb.commons.path.AlignedPath;
 import org.apache.iotdb.commons.path.MeasurementPath;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.path.PathPatternTree;
@@ -50,6 +51,7 @@ import org.apache.iotdb.db.mpp.plan.Coordinator;
 import org.apache.iotdb.db.mpp.plan.execution.ExecutionResult;
 import org.apache.iotdb.db.mpp.plan.expression.Expression;
 import org.apache.iotdb.db.mpp.plan.expression.ExpressionType;
+import org.apache.iotdb.db.mpp.plan.expression.ResultColumn;
 import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand;
 import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.parameter.DeviceViewIntoPathDescriptor;
@@ -64,7 +66,6 @@ import 
org.apache.iotdb.db.mpp.plan.statement.component.FillComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.GroupByTimeComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.IntoComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
-import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn;
 import org.apache.iotdb.db.mpp.plan.statement.component.SortItem;
 import org.apache.iotdb.db.mpp.plan.statement.component.SortKey;
 import org.apache.iotdb.db.mpp.plan.statement.component.WhereCondition;
@@ -194,7 +195,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
       // concat path and construct path pattern tree
       PathPatternTree patternTree = new PathPatternTree();
       queryStatement =
-          (QueryStatement) new ConcatPathRewriter().rewrite(queryStatement, 
patternTree);
+          (QueryStatement) new 
ConcatPathRewriter(analysis).rewrite(queryStatement, patternTree);
       analysis.setStatement(queryStatement);
 
       // request schema fetch API
@@ -229,11 +230,9 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
         analyzeOrderBy(analysis, queryStatement);
         return analyzeLast(analysis, schemaTree.getAllMeasurement(), 
schemaTree);
       }
-
-      List<Pair<Expression, String>> outputExpressions;
       if (queryStatement.isAlignByDevice()) {
         Set<PartialPath> deviceSet = analyzeFrom(queryStatement, schemaTree);
-        outputExpressions = analyzeSelect(analysis, queryStatement, 
schemaTree, deviceSet);
+        analyzeSelect(analysis, queryStatement, schemaTree, deviceSet);
 
         Map<String, Set<Expression>> deviceToAggregationExpressions = new 
HashMap<>();
         analyzeHaving(
@@ -245,17 +244,17 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
         analyzeDeviceToSourceTransform(analysis, queryStatement);
 
         analyzeDeviceToSource(analysis, queryStatement);
-        analyzeDeviceView(analysis, queryStatement, outputExpressions);
+        analyzeDeviceView(analysis, queryStatement);
 
-        analyzeInto(analysis, queryStatement, deviceSet, outputExpressions);
+        analyzeInto(analysis, queryStatement, deviceSet);
       } else {
-        outputExpressions = analyzeSelect(analysis, queryStatement, 
schemaTree);
+        analyzeSelect(analysis, queryStatement, schemaTree);
 
         analyzeHaving(analysis, queryStatement, schemaTree);
-        analyzeGroupByLevel(analysis, queryStatement, outputExpressions);
-        analyzeGroupByTag(analysis, queryStatement, outputExpressions, 
schemaTree);
+        analyzeGroupByLevel(analysis, queryStatement);
+        analyzeGroupByTag(analysis, queryStatement, schemaTree);
         Set<Expression> selectExpressions =
-            outputExpressions.stream()
+            analysis.getOutputExpressions().stream()
                 .map(Pair::getLeft)
                 .collect(Collectors.toCollection(LinkedHashSet::new));
         analysis.setSelectExpressions(selectExpressions);
@@ -267,7 +266,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
 
         analyzeSource(analysis, queryStatement);
 
-        analyzeInto(analysis, queryStatement, outputExpressions);
+        analyzeInto(analysis, queryStatement);
       }
 
       analyzeGroupBy(analysis, queryStatement);
@@ -275,7 +274,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
       analyzeFill(analysis, queryStatement);
 
       // generate result set header according to output expressions
-      analyzeOutput(analysis, queryStatement, outputExpressions);
+      analyzeOutput(analysis, queryStatement);
 
       // fetch partition information
       analyzeDataPartition(analysis, queryStatement, schemaTree);
@@ -367,27 +366,25 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
     return analysis;
   }
 
-  private List<Pair<Expression, String>> analyzeSelect(
+  private void analyzeSelect(
       Analysis analysis, QueryStatement queryStatement, ISchemaTree 
schemaTree) {
-    List<Pair<Expression, String>> outputExpressions = new ArrayList<>();
+    List<Pair<Expression, ResultColumn>> outputExpressions = new ArrayList<>();
     boolean isGroupByLevel = queryStatement.isGroupByLevel();
+    boolean isGroupByTag = queryStatement.isGroupByTag();
     ColumnPaginationController paginationController =
         new ColumnPaginationController(
             queryStatement.getSeriesLimit(),
             queryStatement.getSeriesOffset(),
             queryStatement.isLastQuery() || isGroupByLevel);
-
-    for (ResultColumn resultColumn : 
queryStatement.getSelectComponent().getResultColumns()) {
-      boolean hasAlias = resultColumn.hasAlias();
+    for (Pair<Expression, ResultColumn> resultColumn : 
analysis.getOutputExpressions()) {
+      boolean hasAlias = resultColumn.getRight().hasAlias();
       List<Expression> resultExpressions =
-          
ExpressionAnalyzer.removeWildcardInExpression(resultColumn.getExpression(), 
schemaTree);
-      if (hasAlias
-          && !queryStatement.isGroupByLevel()
-          && !queryStatement.isGroupByTag()
-          && resultExpressions.size() > 1) {
+          
ExpressionAnalyzer.removeWildcardInExpression(resultColumn.getLeft(), 
schemaTree);
+      if (hasAlias && !isGroupByLevel && !isGroupByTag && 
resultExpressions.size() > 1) {
         throw new SemanticException(
             String.format(
-                "alias '%s' can only be matched with one time series", 
resultColumn.getAlias()));
+                "alias '%s' can only be matched with one time series",
+                resultColumn.getRight().getAlias()));
       }
       for (Expression expression : resultExpressions) {
         if (paginationController.hasCurOffset()) {
@@ -397,20 +394,13 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
         if (paginationController.hasCurLimit()) {
           if (isGroupByLevel) {
             analyzeExpression(analysis, expression);
-            outputExpressions.add(new Pair<>(expression, 
resultColumn.getAlias()));
-            queryStatement
-                .getGroupByLevelComponent()
-                .updateIsCountStar(resultColumn.getExpression());
+            outputExpressions.add(new Pair<>(expression, 
resultColumn.getRight()));
+            
queryStatement.getGroupByLevelComponent().updateIsCountStar(resultColumn.getLeft());
           } else {
             Expression expressionWithoutAlias =
                 ExpressionAnalyzer.removeAliasFromExpression(expression);
-            String alias =
-                !Objects.equals(expressionWithoutAlias, expression)
-                    ? expression.getExpressionString()
-                    : null;
-            alias = hasAlias ? resultColumn.getAlias() : alias;
             analyzeExpression(analysis, expressionWithoutAlias);
-            outputExpressions.add(new Pair<>(expressionWithoutAlias, alias));
+            outputExpressions.add(new Pair<>(expressionWithoutAlias, 
resultColumn.getRight()));
           }
           paginationController.consumeLimit();
         } else {
@@ -418,7 +408,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
         }
       }
     }
-    return outputExpressions;
+    analysis.setOutputExpressions(outputExpressions);
   }
 
   private Set<PartialPath> analyzeFrom(QueryStatement queryStatement, 
ISchemaTree schemaTree) {
@@ -436,21 +426,21 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
     return deviceSet;
   }
 
-  private List<Pair<Expression, String>> analyzeSelect(
+  private void analyzeSelect(
       Analysis analysis,
       QueryStatement queryStatement,
       ISchemaTree schemaTree,
       Set<PartialPath> deviceSet) {
-    List<Pair<Expression, String>> outputExpressions = new ArrayList<>();
+    List<Pair<Expression, 
org.apache.iotdb.db.mpp.plan.expression.ResultColumn>> outputExpressions =
+        new ArrayList<>();
     Map<String, Set<Expression>> deviceToSelectExpressions = new HashMap<>();
 
     ColumnPaginationController paginationController =
         new ColumnPaginationController(
             queryStatement.getSeriesLimit(), queryStatement.getSeriesOffset(), 
false);
-
-    for (ResultColumn resultColumn : 
queryStatement.getSelectComponent().getResultColumns()) {
-      Expression selectExpression = resultColumn.getExpression();
-      boolean hasAlias = resultColumn.hasAlias();
+    for (Pair<Expression, ResultColumn> resultColumn : 
analysis.getOutputExpressions()) {
+      Expression selectExpression = resultColumn.getLeft();
+      boolean hasAlias = resultColumn.getRight().hasAlias();
 
       // select expression after removing wildcard
       // use LinkedHashMap for order-preserving
@@ -471,7 +461,8 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
       if (hasAlias && measurementToDeviceSelectExpressions.keySet().size() > 
1) {
         throw new SemanticException(
             String.format(
-                "alias '%s' can only be matched with one time series", 
resultColumn.getAlias()));
+                "alias '%s' can only be matched with one time series",
+                resultColumn.getRight().getAlias()));
       }
 
       for (Expression measurementExpression : 
measurementToDeviceSelectExpressions.keySet()) {
@@ -494,13 +485,9 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
           // add outputExpressions
           Expression measurementExpressionWithoutAlias =
               
ExpressionAnalyzer.removeAliasFromExpression(measurementExpression);
-          String alias =
-              !Objects.equals(measurementExpressionWithoutAlias, 
measurementExpression)
-                  ? measurementExpression.getExpressionString()
-                  : null;
-          alias = hasAlias ? resultColumn.getAlias() : alias;
           analyzeExpression(analysis, measurementExpressionWithoutAlias);
-          outputExpressions.add(new Pair<>(measurementExpressionWithoutAlias, 
alias));
+          outputExpressions.add(
+              new Pair<>(measurementExpressionWithoutAlias, 
resultColumn.getRight()));
 
           // add deviceToSelectExpressions
           for (String deviceName : 
deviceToSelectExpressionsOfOneMeasurement.keySet()) {
@@ -518,9 +505,8 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
         }
       }
     }
-
+    analysis.setOutputExpressions(outputExpressions);
     analysis.setDeviceToSelectExpressions(deviceToSelectExpressions);
-    return outputExpressions;
   }
 
   private void analyzeHaving(
@@ -584,10 +570,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
     analysis.setHavingExpression(havingExpression);
   }
 
-  private void analyzeGroupByLevel(
-      Analysis analysis,
-      QueryStatement queryStatement,
-      List<Pair<Expression, String>> outputExpressions) {
+  private void analyzeGroupByLevel(Analysis analysis, QueryStatement 
queryStatement) {
     if (!queryStatement.isGroupByLevel()) {
       return;
     }
@@ -596,12 +579,12 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
         new 
GroupByLevelController(queryStatement.getGroupByLevelComponent().getLevels());
 
     Set<Expression> groupedSelectExpressions = new LinkedHashSet<>();
-    for (int i = 0; i < outputExpressions.size(); i++) {
-      Pair<Expression, String> expressionAliasPair = outputExpressions.get(i);
+    for (int i = 0; i < analysis.getOutputExpressions().size(); i++) {
+      Pair<Expression, ResultColumn> expressionAliasPair = 
analysis.getOutputExpressions().get(i);
       boolean isCountStar = 
queryStatement.getGroupByLevelComponent().isCountStar(i);
       Expression groupedExpression =
           groupByLevelController.control(
-              isCountStar, expressionAliasPair.left, 
expressionAliasPair.right);
+              isCountStar, expressionAliasPair.left, 
expressionAliasPair.right.getAlias());
       groupedSelectExpressions.add(groupedExpression);
     }
 
@@ -617,7 +600,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
           groupByLevelController.getGroupedExpressionToRawExpressionsMap());
     }
 
-    outputExpressions.clear();
+    analysis.getOutputExpressions().clear();
     ColumnPaginationController paginationController =
         new ColumnPaginationController(
             queryStatement.getSeriesLimit(), queryStatement.getSeriesOffset(), 
false);
@@ -633,7 +616,12 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
                 
groupByLevelController.getAlias(groupedExpression.getExpressionString()));
         Expression groupedExpressionWithoutAlias = outputExpression.left;
         analyzeExpression(analysis, groupedExpressionWithoutAlias);
-        outputExpressions.add(outputExpression);
+        analysis
+            .getOutputExpressions()
+            .add(
+                new Pair<>(
+                    outputExpression.getLeft(),
+                    new ResultColumn(outputExpression.getLeft(), 
outputExpression.getRight())));
         updateGroupByLevelExpressions(
             groupedExpressionWithoutAlias,
             groupByLevelExpressions,
@@ -693,17 +681,14 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
    * <p>TODO: support slimit/soffset/value filter
    */
   private void analyzeGroupByTag(
-      Analysis analysis,
-      QueryStatement queryStatement,
-      List<Pair<Expression, String>> outputExpressions,
-      ISchemaTree schemaTree) {
+      Analysis analysis, QueryStatement queryStatement, ISchemaTree 
schemaTree) {
     if (!queryStatement.isGroupByTag()) {
       return;
     }
     if (analysis.hasValueFilter()) {
       throw new SemanticException("Only time filters are supported in GROUP BY 
TAGS query");
     }
-    Map<List<String>, LinkedHashMap<Expression, List<Expression>>>
+    Map<List<String>, LinkedHashMap<Expression, Set<Expression>>>
         tagValuesToGroupedTimeseriesOperands = new HashMap<>();
     LinkedHashMap<Expression, Set<Expression>> groupByTagOutputExpressions = 
new LinkedHashMap<>();
     List<String> tagKeys = 
queryStatement.getGroupByTagComponent().getTagKeys();
@@ -711,62 +696,90 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
     Map<MeasurementPath, Map<String, String>> queriedTagMap = new HashMap<>();
     allSelectedPath.forEach(v -> queriedTagMap.put(v, v.getTagMap()));
 
-    for (Pair<Expression, String> outputExpressionAndAlias : 
outputExpressions) {
-      if (!(outputExpressionAndAlias.getLeft() instanceof FunctionExpression
-          && outputExpressionAndAlias.getLeft().getExpressions().get(0) 
instanceof TimeSeriesOperand
-          && 
outputExpressionAndAlias.getLeft().isBuiltInAggregationFunctionExpression())) {
-        throw new SemanticException(
-            outputExpressionAndAlias.getLeft()
-                + " can't be used in group by tag. It will be supported in the 
future.");
-      }
-      FunctionExpression outputExpression = (FunctionExpression) 
outputExpressionAndAlias.getLeft();
-      MeasurementPath measurementPath =
-          (MeasurementPath)
-              ((TimeSeriesOperand) 
outputExpression.getExpressions().get(0)).getPath();
-      MeasurementPath fakePath = null;
-      try {
-        fakePath =
-            new MeasurementPath(measurementPath.getMeasurement(), 
measurementPath.getSeriesType());
-      } catch (IllegalPathException e) {
-        // do nothing
+    for (Pair<Expression, ResultColumn> pair : 
analysis.getOutputExpressions()) {
+      List<Expression> aggregationExpressions =
+          ExpressionAnalyzer.searchAggregationExpressions(pair.left);
+      copySeriesDataTypeToExpression(pair.left, pair.right.getExpression());
+      for (Expression exp : aggregationExpressions) {
+        // TODO: avg(s1 + s2) will be supported in the future
+        if (exp.getExpressions().size() > 1) {
+          throw new SemanticException(
+              pair.left + " can't be used in group by tag. It will be 
supported in the future.");
+        }
+        FunctionExpression outputExpression = (FunctionExpression) exp;
+        MeasurementPath measurementPath =
+            (MeasurementPath)
+                ((TimeSeriesOperand) 
outputExpression.getExpressions().get(0)).getPath();
+
+        Expression groupedExpression = pair.getRight().getExpression();
+        groupByTagOutputExpressions
+            .computeIfAbsent(groupedExpression, v -> new HashSet<>())
+            .add(exp);
+        Map<String, String> tagMap = queriedTagMap.get(measurementPath);
+        List<String> tagValues = new ArrayList<>();
+        for (String tagKey : tagKeys) {
+          tagValues.add(tagMap.get(tagKey));
+        }
+        tagValuesToGroupedTimeseriesOperands
+            .computeIfAbsent(tagValues, key -> new LinkedHashMap<>())
+            .computeIfAbsent(groupedExpression, key -> new HashSet<>())
+            .add(outputExpression.getExpressions().get(0));
       }
-      Expression measurementExpression = new TimeSeriesOperand(fakePath);
-      Expression groupedExpression =
-          new FunctionExpression(
-              outputExpression.getFunctionName(),
-              outputExpression.getFunctionAttributes(),
-              Collections.singletonList(measurementExpression));
-      groupByTagOutputExpressions
-          .computeIfAbsent(groupedExpression, v -> new HashSet<>())
-          .add(outputExpression);
-      Map<String, String> tagMap = queriedTagMap.get(measurementPath);
-      List<String> tagValues = new ArrayList<>();
-      for (String tagKey : tagKeys) {
-        tagValues.add(tagMap.get(tagKey));
+    }
+
+    // Keep the original output columns to the final data set
+    List<Pair<Expression, ResultColumn>> outputColumns = new ArrayList<>();
+    for (int i = 0; i < analysis.getOutputExpressions().size(); i++) {
+      // As the expression's equal() compares the expression string instead, 
if user inputs 2 same
+      // expressions in the select clause, they may be de-duplicated by the 
equals() comparison.
+      ResultColumn curRc = analysis.getOutputExpressions().get(i).right;
+      if (i == 0 || curRc != analysis.getOutputExpressions().get(i - 1).right) 
{
+        analyzeExpression(analysis, curRc.getExpression());
+        outputColumns.add(
+            new Pair<>(
+                curRc.getExpression(), new ResultColumn(curRc.getExpression(), 
curRc.getAlias())));
       }
-      tagValuesToGroupedTimeseriesOperands
-          .computeIfAbsent(tagValues, key -> new LinkedHashMap<>())
-          .computeIfAbsent(groupedExpression, key -> new ArrayList<>())
-          .add(outputExpression.getExpressions().get(0));
     }
 
-    outputExpressions.clear();
+    analysis.getOutputExpressions().clear();
     for (String tagKey : tagKeys) {
       Expression tagKeyExpression =
           TimeSeriesOperand.constructColumnHeaderExpression(tagKey, 
TSDataType.TEXT);
       analyzeExpression(analysis, tagKeyExpression);
-      outputExpressions.add(new Pair<>(tagKeyExpression, null));
+      analysis
+          .getOutputExpressions()
+          .add(new Pair<>(tagKeyExpression, new ResultColumn(tagKeyExpression, 
null)));
     }
     for (Expression groupByTagOutputExpression : 
groupByTagOutputExpressions.keySet()) {
-      // TODO: support alias
       analyzeExpression(analysis, groupByTagOutputExpression);
-      outputExpressions.add(new Pair<>(groupByTagOutputExpression, null));
     }
+    analysis.getOutputExpressions().addAll(outputColumns);
     analysis.setTagKeys(queryStatement.getGroupByTagComponent().getTagKeys());
     
analysis.setTagValuesToGroupedTimeseriesOperands(tagValuesToGroupedTimeseriesOperands);
     analysis.setCrossGroupByExpressions(groupByTagOutputExpressions);
   }
 
+  /**
+   * Copy a measurement type from source expression to target expression 
recursively. The source and
+   * target expressions should have the same format in expression. e.g. 
avg(s1) + 1 + sum(s2) has
+   * the same format with avg(root.sg.d1.s1) + 1 + sum(root.sg.d1.s2), but 
avg(root.sg.d1.s1) + 1 +
+   * min_value(root.sg.d1.s2) doesn't. source should be a {@link 
MeasurementPath} or {@link
+   * AlignedPath}.
+   */
+  private void copySeriesDataTypeToExpression(Expression source, Expression 
target) {
+    if (source instanceof TimeSeriesOperand) {
+      TimeSeriesOperand sourceExp = (TimeSeriesOperand) source;
+      TimeSeriesOperand targetExp = (TimeSeriesOperand) target;
+      targetExp.setPath(
+          new MeasurementPath(targetExp.getPath(), 
sourceExp.getPath().getSeriesType()));
+      return;
+    }
+    for (int i = 0; i < source.getExpressions().size(); i++) {
+      copySeriesDataTypeToExpression(
+          source.getExpressions().get(i), target.getExpressions().get(i));
+    }
+  }
+
   private void analyzeDeviceToAggregation(
       Analysis analysis,
       QueryStatement queryStatement,
@@ -942,10 +955,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
         conJunctions.stream().distinct().collect(Collectors.toList()));
   }
 
-  private void analyzeDeviceView(
-      Analysis analysis,
-      QueryStatement queryStatement,
-      List<Pair<Expression, String>> outputExpressions) {
+  private void analyzeDeviceView(Analysis analysis, QueryStatement 
queryStatement) {
     Expression deviceExpression =
         new TimeSeriesOperand(
             new MeasurementPath(new PartialPath(COLUMN_DEVICE, false), 
TSDataType.TEXT));
@@ -953,7 +963,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
     Set<Expression> selectExpressions = new LinkedHashSet<>();
     selectExpressions.add(deviceExpression);
     selectExpressions.addAll(
-        outputExpressions.stream()
+        analysis.getOutputExpressions().stream()
             .map(Pair::getLeft)
             .collect(Collectors.toCollection(LinkedHashSet::new)));
     analysis.setSelectExpressions(selectExpressions);
@@ -1008,10 +1018,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
     analysis.setDeviceViewInputIndexesMap(deviceViewInputIndexesMap);
   }
 
-  private void analyzeOutput(
-      Analysis analysis,
-      QueryStatement queryStatement,
-      List<Pair<Expression, String>> outputExpressions) {
+  private void analyzeOutput(Analysis analysis, QueryStatement queryStatement) 
{
     if (queryStatement.isSelectInto()) {
       analysis.setRespDatasetHeader(
           
DatasetHeaderFactory.getSelectIntoHeader(queryStatement.isAlignByDevice()));
@@ -1025,11 +1032,11 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
       columnHeaders.add(new ColumnHeader(COLUMN_DEVICE, TSDataType.TEXT, 
null));
     }
     columnHeaders.addAll(
-        outputExpressions.stream()
+        analysis.getOutputExpressions().stream()
             .map(
                 expressionAliasPair -> {
                   String columnName = 
expressionAliasPair.left.getExpressionString();
-                  String alias = expressionAliasPair.right;
+                  String alias = expressionAliasPair.right.getAlias();
                   return new ColumnHeader(
                       columnName, analysis.getType(expressionAliasPair.left), 
alias);
                 })
@@ -1095,17 +1102,14 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
   }
 
   private void analyzeInto(
-      Analysis analysis,
-      QueryStatement queryStatement,
-      Set<PartialPath> deviceSet,
-      List<Pair<Expression, String>> outputExpressions) {
+      Analysis analysis, QueryStatement queryStatement, Set<PartialPath> 
deviceSet) {
     if (!queryStatement.isSelectInto()) {
       return;
     }
 
     List<PartialPath> sourceDevices = new ArrayList<>(deviceSet);
     List<Expression> sourceColumns =
-        outputExpressions.stream()
+        analysis.getOutputExpressions().stream()
             .map(Pair::getLeft)
             .collect(Collectors.toCollection(ArrayList::new));
 
@@ -1142,16 +1146,13 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
     analysis.setDeviceViewIntoPathDescriptor(deviceViewIntoPathDescriptor);
   }
 
-  private void analyzeInto(
-      Analysis analysis,
-      QueryStatement queryStatement,
-      List<Pair<Expression, String>> outputExpressions) {
+  private void analyzeInto(Analysis analysis, QueryStatement queryStatement) {
     if (!queryStatement.isSelectInto()) {
       return;
     }
 
     List<Expression> sourceColumns =
-        outputExpressions.stream()
+        analysis.getOutputExpressions().stream()
             .map(Pair::getLeft)
             .collect(Collectors.toCollection(ArrayList::new));
 
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java
index 71273d5c12..9a5e6216bc 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java
@@ -27,10 +27,10 @@ import org.apache.iotdb.db.mpp.plan.statement.Statement;
 import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn;
 import org.apache.iotdb.db.mpp.plan.statement.component.SelectComponent;
 import org.apache.iotdb.db.mpp.plan.statement.crud.QueryStatement;
+import org.apache.iotdb.tsfile.utils.Pair;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * This rewriter:
@@ -43,6 +43,11 @@ import java.util.stream.Collectors;
 public class ConcatPathRewriter {
 
   private PathPatternTree patternTree;
+  private final Analysis analysis;
+
+  public ConcatPathRewriter(Analysis analysis) {
+    this.analysis = analysis;
+  }
 
   public Statement rewrite(Statement statement, PathPatternTree patternTree)
       throws StatementAnalyzeException {
@@ -53,12 +58,21 @@ public class ConcatPathRewriter {
     List<PartialPath> prefixPaths = 
queryStatement.getFromComponent().getPrefixPaths();
 
     if (queryStatement.isAlignByDevice()) {
-      queryStatement.getSelectComponent().getResultColumns().stream()
-          .map(ResultColumn::getExpression)
+      queryStatement
+          .getSelectComponent()
+          .getResultColumns()
           .forEach(
-              expression ->
-                  ExpressionAnalyzer.constructPatternTreeFromExpression(
-                      expression, prefixPaths, patternTree));
+              resultColumn -> {
+                ExpressionAnalyzer.constructPatternTreeFromExpression(
+                    resultColumn.getExpression(), prefixPaths, patternTree);
+                analysis
+                    .getOutputExpressions()
+                    .add(
+                        new Pair<>(
+                            resultColumn.getExpression(),
+                            new 
org.apache.iotdb.db.mpp.plan.expression.ResultColumn(
+                                resultColumn.getExpression(), 
resultColumn.getAlias())));
+              });
     } else {
       // concat SELECT with FROM
       List<ResultColumn> resultColumns =
@@ -93,6 +107,9 @@ public class ConcatPathRewriter {
     List<ResultColumn> resultColumns = new ArrayList<>();
     for (ResultColumn resultColumn : selectComponent.getResultColumns()) {
       boolean needAliasCheck = resultColumn.hasAlias() && !isGroupByLevel;
+      org.apache.iotdb.db.mpp.plan.expression.ResultColumn 
originalResultColumn =
+          new org.apache.iotdb.db.mpp.plan.expression.ResultColumn(
+              resultColumn.getExpression(), resultColumn.getAlias());
       List<Expression> resultExpressions =
           ExpressionAnalyzer.concatExpressionWithSuffixPaths(
               resultColumn.getExpression(), prefixPaths, patternTree);
@@ -101,13 +118,13 @@ public class ConcatPathRewriter {
             String.format(
                 "alias '%s' can only be matched with one time series", 
resultColumn.getAlias()));
       }
-      resultColumns.addAll(
-          resultExpressions.stream()
-              .map(
-                  expression ->
-                      new ResultColumn(
-                          expression, resultColumn.getAlias(), 
resultColumn.getColumnType()))
-              .collect(Collectors.toList()));
+      resultExpressions.forEach(
+          expression -> {
+            resultColumns.add(
+                new ResultColumn(
+                    expression, resultColumn.getAlias(), 
resultColumn.getColumnType()));
+            analysis.getOutputExpressions().add(new Pair<>(expression, 
originalResultColumn));
+          });
     }
     return resultColumns;
   }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java
index 6f7087c10c..b26276a7c6 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java
@@ -223,7 +223,7 @@ public class LogicalPlanBuilder {
       Set<Expression> sourceTransformExpressions,
       LinkedHashMap<Expression, Set<Expression>> crossGroupByAggregations,
       List<String> tagKeys,
-      Map<List<String>, LinkedHashMap<Expression, List<Expression>>>
+      Map<List<String>, LinkedHashMap<Expression, Set<Expression>>>
           tagValuesToGroupedTimeseriesOperands) {
     boolean needCheckAscending = groupByTimeParameter == null;
     Map<PartialPath, List<AggregationDescriptor>> ascendingAggregations = new 
HashMap<>();
@@ -400,7 +400,7 @@ public class LogicalPlanBuilder {
       Set<Expression> aggregationExpressions,
       LinkedHashMap<Expression, Set<Expression>> crossGroupByExpressions,
       List<String> tagKeys,
-      Map<List<String>, LinkedHashMap<Expression, List<Expression>>>
+      Map<List<String>, LinkedHashMap<Expression, Set<Expression>>>
           tagValuesToGroupedTimeseriesOperands) {
     if (curStep.isOutputPartial()) {
       if (groupByTimeParameter != null && groupByTimeParameter.hasOverlap()) {
@@ -645,7 +645,7 @@ public class LogicalPlanBuilder {
 
   private PlanNode createGroupByTagNode(
       List<String> tagKeys,
-      Map<List<String>, LinkedHashMap<Expression, List<Expression>>>
+      Map<List<String>, LinkedHashMap<Expression, Set<Expression>>>
           tagValuesToGroupedTimeseriesOperands,
       Collection<Expression> groupByTagOutputExpressions,
       List<PlanNode> children,
@@ -655,7 +655,7 @@ public class LogicalPlanBuilder {
     SortedMap<List<String>, List<CrossSeriesAggregationDescriptor>>
         tagValuesToAggregationDescriptors = new 
TreeMap<>(GroupByTagNode::tagValuesComparator);
     for (List<String> tagValues : 
tagValuesToGroupedTimeseriesOperands.keySet()) {
-      LinkedHashMap<Expression, List<Expression>> groupedTimeseriesOperands =
+      LinkedHashMap<Expression, Set<Expression>> groupedTimeseriesOperands =
           tagValuesToGroupedTimeseriesOperands.get(tagValues);
       List<CrossSeriesAggregationDescriptor> aggregationDescriptors = new 
ArrayList<>();
 
@@ -667,12 +667,12 @@ public class LogicalPlanBuilder {
         }
         Expression next = iter.next();
         if (next.equals(groupByTagOutputExpression)) {
-          String functionName = ((FunctionExpression) 
next).getFunctionName().toUpperCase();
+          String functionName = ((FunctionExpression) 
next).getFunctionName().toLowerCase();
           CrossSeriesAggregationDescriptor aggregationDescriptor =
               new CrossSeriesAggregationDescriptor(
                   functionName,
                   curStep,
-                  groupedTimeseriesOperands.get(next),
+                  new ArrayList<>(groupedTimeseriesOperands.get(next)),
                   next.getExpressions().get(0));
           aggregationDescriptors.add(aggregationDescriptor);
         } else {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
index e894c70d06..f6cfa83493 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
@@ -325,7 +325,7 @@ public class QueryStatement extends Statement {
           throw new SemanticException("Raw data and aggregation hybrid query 
is not supported.");
         }
         outputColumn.add(
-            resultColumn.getAlias() != null
+            resultColumn.hasAlias()
                 ? resultColumn.getAlias()
                 : resultColumn.getExpression().getExpressionString());
       }

Reply via email to