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

weihao pushed a commit to branch last-alias-dev1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit ced44bba1967a79330fa71bfd653cd2ffe8f49c0
Author: Weihao Li <[email protected]>
AuthorDate: Thu Jan 29 17:44:02 2026 +0800

    Fix timeseries alias display in last for TreeModel (#17109)
    
    (cherry picked from commit 5f641b262c33b160e6231adfe955342e8e3ca108)
    Signed-off-by: Weihao Li <[email protected]>
    
    # Conflicts:
    #       
integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedLastQueryIT.java
    #       
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/AlignedUpdateViewPathLastCacheOperator.java
    #       
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
    #       
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
    #       
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
    #       
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java
    #       
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/last/LastQueryNode.java
    #       
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java
    #       
iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/LastQueryTest.java
    #       
iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/DataQueryLogicalPlannerTest.java
    #       
iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/source/LastQueryScanNodeSerdeTest.java
---
 .../db/it/aligned/IoTDBAlignedLastQueryIT.java     | 114 ++++++++++++++
 .../iotdb/db/it/last/IoTDBLastQueryAlias2IT.java   |  60 ++++++++
 .../iotdb/db/it/last/IoTDBLastQueryAliasIT.java    | 164 +++++++++++++++++++++
 .../AlignedUpdateViewPathLastCacheOperator.java    |  25 +++-
 .../process/last/UpdateLastCacheOperator.java      |   2 +-
 .../last/UpdateViewPathLastCacheOperator.java      |   2 +-
 .../queryengine/plan/analyze/AnalyzeVisitor.java   |   6 +-
 .../plan/planner/LogicalPlanBuilder.java           |  47 +++++-
 .../plan/planner/OperatorTreeGenerator.java        |  23 ++-
 .../plan/planner/plan/node/PlanGraphPrinter.java   |   7 +-
 .../plan/node/process/last/LastQueryNode.java      |  11 +-
 .../plan/node/source/LastQueryScanNode.java        |  99 +++++++++----
 .../plan/planner/distribution/LastQueryTest.java   |   9 +-
 .../logical/DataQueryLogicalPlannerTest.java       |  11 +-
 .../node/source/LastQueryScanNodeSerdeTest.java    |   2 +
 15 files changed, 517 insertions(+), 65 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedLastQueryIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedLastQueryIT.java
index a32572d4778..214a2c31806 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedLastQueryIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedLastQueryIT.java
@@ -340,4 +340,118 @@ public class IoTDBAlignedLastQueryIT {
     selectSomeAlignedLastWithTimeFilterTest();
     selectSomeAlignedAndNonAlignedLastWithTimeFilterTest();
   }
+
+  @Test
+  public void testLastAlias() {
+    String[] sqls =
+        new String[] {
+          "create aligned timeseries root.ln_1.tb_6141(fengjituichu_BOOLEAN 
BOOLEAN encoding=RLE,`chushuiNH4-N_DOUBLE` DOUBLE 
encoding=GORILLA,mochanshuizhuangtai_BOOLEAN BOOLEAN encoding=RLE,11_TEXT TEXT 
encoding=PLAIN,chanshuijianxieyunxingshijianshezhi_DOUBLE DOUBLE 
encoding=GORILLA,wenben_TEXT TEXT encoding=PLAIN, fengjitouru_BOOLEAN BOOLEAN 
encoding=RLE,meiju_INT32 INT32 encoding=RLE,chushuiTP_DOUBLE DOUBLE 
encoding=GORILLA,shuiguanliusu_DOUBLE DOUBLE encoding=GORILLA,CO2_DOUBLE DOU 
[...]
+          "alter timeseries root.ln_1.tb_6141.fengjituichu_BOOLEAN upsert 
alias=fengjituichu;",
+          "alter timeseries root.ln_1.tb_6141.shuiguanliusu_DOUBLE upsert 
alias=shuiguanliusu;",
+          "alter timeseries root.ln_1.tb_6141.CO2_DOUBLE upsert alias=CO2;",
+          "alter timeseries root.ln_1.tb_6141.fengjitouru_BOOLEAN upsert 
alias=fengjitouru;",
+          "alter timeseries 
root.ln_1.tb_6141.chanshuijianxieyunxingshijianshezhi_DOUBLE upsert 
alias=chanshuijianxieyunxingshijianshezhi;",
+          "alter timeseries root.ln_1.tb_6141.mochanshuizhuangtai_BOOLEAN 
upsert alias=mochanshuizhuangtai;",
+          "alter timeseries root.ln_1.tb_6141.meiju_INT32 upsert alias=meiju;",
+          "alter timeseries root.ln_1.tb_6141.chushuiTP_DOUBLE upsert 
alias=chushuiTP;",
+          "alter timeseries root.ln_1.tb_6141.wenben_TEXT upsert 
alias=wenben;",
+          "alter timeseries root.ln_1.tb_6141.`chushuiNH4-N_DOUBLE` upsert 
alias=`chushuiNH4-N`;",
+          "alter timeseries root.ln_1.tb_6141.gongnengma_DOUBLE upsert 
alias=gongnengma;",
+          "alter timeseries root.ln_1.tb_6141.11_TEXT upsert alias=`11`;",
+          "alter timeseries root.ln_1.tb_6141.`kaiguanliang-yunxing_BOOLEAN` 
upsert alias=`kaiguanliang-yunxing`;",
+          "insert into 
root.ln_1.tb_6141(time,chanshuijianxieyunxingshijianshezhi_DOUBLE) aligned 
values(1679365910000,10.0);",
+          "insert into root.ln_1.tb_6141(time,chushuiTP_DOUBLE) aligned 
values(1679365910000,15.0);",
+          "insert into root.ln_1.tb_6141(time,gongnengma_DOUBLE) aligned 
values(1679477545000,2.0);",
+          "insert into root.ln_1.tb_6141(time,wenben_TEXT) aligned 
values(1675995566000,52);",
+          "insert into root.ln_1.tb_6141(time,meiju_INT32) aligned 
values(1675995566000,2);",
+          "insert into root.ln_1.tb_6141(time,shuiguanliusu_DOUBLE) aligned 
values(1679365910000,15.0);",
+          "insert into root.ln_1.tb_6141(time,mochanshuizhuangtai_BOOLEAN) 
aligned values(1677033625000,true);",
+          "insert into root.ln_1.tb_6141(time,fengjitouru_BOOLEAN) aligned 
values(1675995566000,true);",
+          "insert into root.ln_1.tb_6141(time,fengjituichu_BOOLEAN) aligned 
values(1675995566000,false);",
+          "insert into root.ln_1.tb_6141(time,11_TEXT) aligned 
values(1679365910000,13);",
+          "insert into root.ln_1.tb_6141(time,CO2_DOUBLE) aligned 
values(1679365910000,12.0);",
+          "insert into root.ln_1.tb_6141(time,`chushuiNH4-N_DOUBLE`) aligned 
values(1679365910000,12.0);",
+          "insert into root.ln_1.tb_6141(time,`kaiguanliang-yunxing_BOOLEAN`) 
aligned values(1675995566000,false);",
+        };
+
+    try (Connection connection = EnvFactory.getEnv().getConnection();
+        Statement statement = connection.createStatement()) {
+
+      // create aligned and non-aligned time series
+      for (String sql : sqls) {
+        statement.addBatch(sql);
+      }
+      statement.executeBatch();
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+
+    Set<String> retSet =
+        new HashSet<>(
+            Arrays.asList(
+                "1679477545000,root.ln_1.tb_6141.gongnengma,2.0,DOUBLE",
+                "1675995566000,root.ln_1.tb_6141.wenben,52,TEXT"));
+
+    try (Connection connection = EnvFactory.getEnv().getConnection();
+        Statement statement = connection.createStatement()) {
+
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "select last gongnengma,wenben from root.ln_1.tb_6141 order by 
timeseries asc;")) {
+        int cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(TIMESERIES_STR)
+                  + ","
+                  + resultSet.getString(VALUE_STR)
+                  + ","
+                  + resultSet.getString(DATA_TYPE_STR);
+          assertTrue(ans, retSet.contains(ans));
+          cnt++;
+        }
+        assertEquals(retSet.size(), cnt);
+      }
+
+    } catch (SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+
+    retSet =
+        new HashSet<>(
+            Arrays.asList(
+                "1679477545000,root.ln_1.tb_6141.gongnengma,2.0,DOUBLE",
+                
"1677033625000,root.ln_1.tb_6141.mochanshuizhuangtai,true,BOOLEAN",
+                "1675995566000,root.ln_1.tb_6141.wenben,52,TEXT"));
+
+    try (Connection connection = EnvFactory.getEnv().getConnection();
+        Statement statement = connection.createStatement()) {
+
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "select last gongnengma,mochanshuizhuangtai,wenben from 
root.ln_1.tb_6141 order by timeseries asc;")) {
+        int cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(TIMESERIES_STR)
+                  + ","
+                  + resultSet.getString(VALUE_STR)
+                  + ","
+                  + resultSet.getString(DATA_TYPE_STR);
+          assertTrue(ans, retSet.contains(ans));
+          cnt++;
+        }
+        assertEquals(retSet.size(), cnt);
+      }
+
+    } catch (SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
 }
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/last/IoTDBLastQueryAlias2IT.java
 
b/integration-test/src/test/java/org/apache/iotdb/db/it/last/IoTDBLastQueryAlias2IT.java
new file mode 100644
index 00000000000..6e8f2753fa8
--- /dev/null
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/last/IoTDBLastQueryAlias2IT.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.it.last;
+
+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 java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.prepareData;
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBLastQueryAlias2IT extends IoTDBLastQueryAliasIT {
+  @BeforeClass
+  public static void setUp() throws Exception {
+    // with lastCache
+    EnvFactory.getEnv().getConfig().getCommonConfig().setEnableLastCache(true);
+    EnvFactory.getEnv().initClusterEnvironment();
+    try (Connection connection = EnvFactory.getEnv().getConnection();
+        Statement statement = connection.createStatement()) {
+      prepareData(SQLs);
+    } catch (SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+}
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/last/IoTDBLastQueryAliasIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/db/it/last/IoTDBLastQueryAliasIT.java
new file mode 100644
index 00000000000..4772804f384
--- /dev/null
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/last/IoTDBLastQueryAliasIT.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.it.last;
+
+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.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.prepareData;
+import static org.apache.iotdb.db.it.utils.TestUtils.resultSetEqualTest;
+import static org.apache.iotdb.itbase.constant.TestConstant.DATA_TYPE_STR;
+import static org.apache.iotdb.itbase.constant.TestConstant.TIMESERIES_STR;
+import static org.apache.iotdb.itbase.constant.TestConstant.TIMESTAMP_STR;
+import static org.apache.iotdb.itbase.constant.TestConstant.VALUE_STR;
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBLastQueryAliasIT {
+  protected static final String[] SQLs =
+      new String[] {
+        "create timeseries root.test.d1.s1(alias3) with dataType= int32",
+        "create timeseries root.test.d1.s2(alias2) with dataType= int32",
+        "create timeseries root.test.d1.s3(alias1) with dataType= int32",
+        "insert into root.test.d1(timestamp,s1,s2,s3) values(1,1,2,3)",
+        "create aligned timeseries root.test.d2 (s1 (alias3) int32,s2 (alias2) 
int32,s3 (alias1) int32)",
+        "insert into root.test.d2(timestamp,s1,s2,s3) values(2,2,3,4)",
+      };
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    // without lastCache
+    
EnvFactory.getEnv().getConfig().getCommonConfig().setEnableLastCache(false);
+    EnvFactory.getEnv().initClusterEnvironment();
+    try (Connection connection = EnvFactory.getEnv().getConnection();
+        Statement statement = connection.createStatement()) {
+      prepareData(SQLs);
+    } catch (SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void nonAlignedTest() {
+    String[] expectedHeader =
+        new String[] {TIMESTAMP_STR, TIMESERIES_STR, VALUE_STR, DATA_TYPE_STR};
+
+    String[] retArray =
+        new String[] {
+          "1,root.test.d1.alias3,1,INT32,",
+        };
+    resultSetEqualTest("select last alias3 from root.test.d1", expectedHeader, 
retArray);
+
+    retArray =
+        new String[] {
+          "1,root.test.d1.alias3,1,INT32,",
+          "1,root.test.d1.alias2,2,INT32,",
+          "1,root.test.d1.alias1,3,INT32,",
+        };
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.d1", expectedHeader, 
retArray);
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.d1", expectedHeader, 
retArray);
+
+    retArray =
+        new String[] {
+          "1,root.test.d1.alias1,3,INT32,",
+          "1,root.test.d1.alias2,2,INT32,",
+          "1,root.test.d1.alias3,1,INT32,",
+        };
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.d1 order by 
timeseries",
+        expectedHeader,
+        retArray);
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.d1 order by 
timeseries",
+        expectedHeader,
+        retArray);
+  }
+
+  @Test
+  public void alignedTest() {
+    String[] expectedHeader =
+        new String[] {TIMESTAMP_STR, TIMESERIES_STR, VALUE_STR, DATA_TYPE_STR};
+    String[] retArray =
+        new String[] {
+          "2,root.test.d2.alias3,2,INT32,",
+          "2,root.test.d2.alias2,3,INT32,",
+          "2,root.test.d2.alias1,4,INT32,",
+        };
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.d2", expectedHeader, 
retArray);
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.d2", expectedHeader, 
retArray);
+
+    retArray =
+        new String[] {
+          "2,root.test.d2.alias1,4,INT32,",
+          "2,root.test.d2.alias2,3,INT32,",
+          "2,root.test.d2.alias3,2,INT32,",
+        };
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.d2 order by 
timeseries",
+        expectedHeader,
+        retArray);
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.d2 order by 
timeseries",
+        expectedHeader,
+        retArray);
+  }
+
+  @Test
+  public void mixedTest() {
+    String[] expectedHeader =
+        new String[] {TIMESTAMP_STR, TIMESERIES_STR, VALUE_STR, DATA_TYPE_STR};
+    String[] retArray =
+        new String[] {
+          "1,root.test.d1.alias1,3,INT32,",
+          "1,root.test.d1.alias2,2,INT32,",
+          "1,root.test.d1.alias3,1,INT32,",
+          "2,root.test.d2.alias1,4,INT32,",
+          "2,root.test.d2.alias2,3,INT32,",
+          "2,root.test.d2.alias3,2,INT32,",
+        };
+    resultSetEqualTest(
+        "select last alias3,alias2,alias1 from root.test.* order by 
timeseries",
+        expectedHeader,
+        retArray);
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/AlignedUpdateViewPathLastCacheOperator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/AlignedUpdateViewPathLastCacheOperator.java
index 3fc9f0412bc..5d5afbedb5b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/AlignedUpdateViewPathLastCacheOperator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/AlignedUpdateViewPathLastCacheOperator.java
@@ -28,11 +28,15 @@ import 
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.DataNodeSchemaC
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.TsPrimitiveType;
 
+import java.util.List;
+
 import static com.google.common.base.Preconditions.checkArgument;
 
 public class AlignedUpdateViewPathLastCacheOperator extends 
AlignedUpdateLastCacheOperator {
-
-  private final String outputViewPath;
+  // Now not only a view path will be set here, but also the measurement path 
with alias will be set
+  // .e.g last query path: root.test.d1(s1(alias1), s2), outputPaths: 
[root.test.d1.alias1, null]
+  private final List<String> outputPaths;
+  private int outputPathIndex = 0;
 
   public AlignedUpdateViewPathLastCacheOperator(
       OperatorContext operatorContext,
@@ -41,7 +45,7 @@ public class AlignedUpdateViewPathLastCacheOperator extends 
AlignedUpdateLastCac
       DataNodeSchemaCache dataNodeSchemaCache,
       boolean needUpdateCache,
       boolean needUpdateNullEntry,
-      String outputViewPath,
+      List<String> outputPaths,
       boolean deviceInMultiRegion) {
     super(
         operatorContext,
@@ -51,19 +55,26 @@ public class AlignedUpdateViewPathLastCacheOperator extends 
AlignedUpdateLastCac
         needUpdateCache,
         needUpdateNullEntry,
         deviceInMultiRegion);
-    checkArgument(seriesPath.getMeasurementList().size() == 1);
-    this.outputViewPath = outputViewPath;
+    checkArgument(outputPaths != null, "outputPaths shouldn't be null");
+    this.outputPaths = outputPaths;
   }
 
   @Override
   protected void appendLastValueToTsBlockBuilder(
       long lastTime, TsPrimitiveType lastValue, MeasurementPath 
measurementPath, String type) {
+    String outputPath = outputPaths.get(outputPathIndex);
     LastQueryUtil.appendLastValue(
-        tsBlockBuilder, lastTime, outputViewPath, lastValue.getStringValue(), 
type);
+        tsBlockBuilder,
+        lastTime,
+        outputPath == null ? measurementPath.getFullPath() : outputPath,
+        lastValue.getStringValue(),
+        type);
+    outputPathIndex++;
   }
 
   @Override
   public long ramBytesUsed() {
-    return super.ramBytesUsed() + RamUsageEstimator.sizeOf(outputViewPath);
+    return super.ramBytesUsed()
+        + outputPaths.stream().mapToLong(path -> 
RamUsageEstimator.sizeOf(path)).sum();
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/UpdateLastCacheOperator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/UpdateLastCacheOperator.java
index d55f1d9fd4e..3e8a92ded26 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/UpdateLastCacheOperator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/UpdateLastCacheOperator.java
@@ -40,7 +40,7 @@ public class UpdateLastCacheOperator extends 
AbstractUpdateLastCacheOperator {
   // fullPath for queried time series
   // It should be exact PartialPath, neither MeasurementPath nor AlignedPath, 
because lastCache only
   // accept PartialPath
-  private final MeasurementPath fullPath;
+  protected final MeasurementPath fullPath;
 
   // type for queried time series
   protected final String dataType;
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/UpdateViewPathLastCacheOperator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/UpdateViewPathLastCacheOperator.java
index dcc83bddfbd..d3ed5b2a7b1 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/UpdateViewPathLastCacheOperator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/last/UpdateViewPathLastCacheOperator.java
@@ -29,7 +29,7 @@ import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.TsPrimitiveType;
 
 public class UpdateViewPathLastCacheOperator extends UpdateLastCacheOperator {
-
+  // Now not only a view path will be set here, but also the measurement path 
with alias will be set
   private final String outputViewPath;
 
   public UpdateViewPathLastCacheOperator(
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 e79fe9e3f40..a28ca79c956 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
@@ -657,7 +657,11 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
                       timeseriesOrdering != null
                           ? new 
TreeMap<>(timeseriesOrdering.getStringComparator())
                           : new LinkedHashMap<>())
-              .put(outputPath.getMeasurement(), timeSeriesOperand);
+              .put(
+                  outputPath.isMeasurementAliasExists()
+                      ? outputPath.getMeasurementAlias()
+                      : outputPath.getMeasurement(),
+                  timeSeriesOperand);
         } else {
           lastQueryNonWritableViewSourceExpressionMap =
               lastQueryNonWritableViewSourceExpressionMap == null
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 1d206b03ca1..81afda469c3 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
@@ -252,10 +252,24 @@ public class LogicalPlanBuilder {
         for (Expression sourceExpression : 
measurementToExpressionsOfDevice.values()) {
           MeasurementPath selectedPath =
               (MeasurementPath) ((TimeSeriesOperand) 
sourceExpression).getPath();
-          String outputViewPath =
-              sourceExpression.isViewExpression()
-                  ? sourceExpression.getViewPath().getFullPath()
-                  : null;
+
+          String outputPath;
+          TSDataType outputViewPathType = null;
+          // the path is view, use the view path as the output path
+          if (sourceExpression.isViewExpression()) {
+            outputPath = sourceExpression.getViewPath().getFullPath();
+            outputViewPathType = selectedPath.getSeriesType();
+          } else {
+            outputPath = selectedPath.getFullPath();
+          }
+          // the path has alias, use alias as the output path
+          if (selectedPath.isMeasurementAliasExists()) {
+            outputPath =
+                selectedPath
+                    .getDevicePath()
+                    
.concatAsMeasurementPath(selectedPath.getMeasurementAlias())
+                    .toString();
+          }
 
           PartialPath devicePath = selectedPath.getDevicePath();
           // For expression with view path, we do not use the deviceId in 
Map.Entry because it is a
@@ -267,7 +281,8 @@ public class LogicalPlanBuilder {
                   devicePath,
                   selectedPath.isUnderAlignedEntity(),
                   
Collections.singletonList(selectedPath.getMeasurementSchema()),
-                  outputViewPath);
+                  Collections.singletonList(outputPath),
+                  outputViewPathType != null);
           this.context.reserveMemoryForFrontEnd(memCost);
         }
       } else {
@@ -275,12 +290,31 @@ public class LogicalPlanBuilder {
         List<IMeasurementSchema> measurementSchemas =
             new ArrayList<>(measurementToExpressionsOfDevice.size());
         PartialPath devicePath = null;
+        List<String> outputPaths = null;
+        int i = 0;
         for (Expression sourceExpression : 
measurementToExpressionsOfDevice.values()) {
           MeasurementPath selectedPath =
               (MeasurementPath) ((TimeSeriesOperand) 
sourceExpression).getPath();
           aligned = selectedPath.isUnderAlignedEntity();
           devicePath = devicePath == null ? selectedPath.getDevicePath() : 
devicePath;
           measurementSchemas.add(selectedPath.getMeasurementSchema());
+
+          // series has alias and use alias to SELECT
+          if (selectedPath.isMeasurementAliasExists()) {
+            if (outputPaths == null) {
+              // fill null as default value
+              outputPaths =
+                  new ArrayList<>(
+                      
Collections.nCopies(measurementToExpressionsOfDevice.size(), null));
+            }
+            outputPaths.set(
+                i,
+                selectedPath
+                    .getDevicePath()
+                    
.concatAsMeasurementPath(selectedPath.getMeasurementAlias())
+                    .toString());
+          }
+          i++;
         }
         // DeviceId is needed in the distribution plan stage
         devicePath.setIDeviceID(deviceId);
@@ -290,7 +324,8 @@ public class LogicalPlanBuilder {
                 devicePath,
                 aligned,
                 measurementSchemas,
-                null);
+                outputPaths,
+                false);
         this.context.reserveMemoryForFrontEnd(memCost);
       }
     }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
index 8aad87866b8..e0aeacb7f56 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
@@ -2789,7 +2789,7 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
               fullPath);
     }
 
-    return Objects.isNull(node.getOutputViewPath())
+    return Objects.isNull(node.getOutputPaths())
         ? new UpdateLastCacheOperator(
             operatorContext,
             lastQueryScan,
@@ -2806,11 +2806,11 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
             DATA_NODE_SCHEMA_CACHE,
             context.isNeedUpdateLastCache(),
             context.isNeedUpdateNullEntry(),
-            node.getOutputViewPath());
+            node.getOutputPaths().get(idx));
   }
 
   private AlignedUpdateLastCacheOperator createAlignedUpdateLastCacheOperator(
-      final String outputViewPath,
+      final List<String> outputPaths,
       final PlanNodeId planNodeId,
       final AlignedPath unCachedPath,
       final LocalExecutionPlanContext context,
@@ -2846,7 +2846,7 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
       }
     }
 
-    return Objects.isNull(outputViewPath)
+    return Objects.isNull(outputPaths)
         ? new AlignedUpdateLastCacheOperator(
             operatorContext,
             lastQueryScan,
@@ -2862,7 +2862,7 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
             DATA_NODE_SCHEMA_CACHE,
             context.isNeedUpdateLastCache(),
             context.isNeedUpdateNullEntry(),
-            outputViewPath,
+            outputPaths,
             deviceInMultiRegion);
   }
 
@@ -2964,6 +2964,7 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
         updateFilterUsingTTL(
             context.getGlobalTimeFilter(),
             DataNodeTTLCache.getInstance().getTTL(devicePath.getNodes()));
+    boolean hasOutputPath = node.getOutputPaths() != null;
     for (int i = 0; i < idxOfMeasurementSchemas.size(); i++) {
       IMeasurementSchema measurementSchema = node.getMeasurementSchema(i);
       final PartialPath measurementPath =
@@ -2994,8 +2995,9 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
           unCachedMeasurementIndexes.add(i);
         }
       } else { //  cached last value is satisfied, put it into 
LastCacheScanOperator
-        if (node.getOutputViewPath() != null) {
-          context.addCachedLastValue(timeValuePair, node.getOutputViewPath());
+        String outputPath = hasOutputPath ? node.getOutputPaths().get(i) : 
null;
+        if (outputPath != null) {
+          context.addCachedLastValue(timeValuePair, 
node.getOutputPaths().get(i));
         } else {
           context.addCachedLastValue(timeValuePair, 
measurementPath.getFullPath());
         }
@@ -3006,12 +3008,17 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
     }
     if (node.isAligned()) {
       AlignedPath unCachedPath = new AlignedPath(node.getDevicePath());
+      // select output paths for uncached measurements
+      List<String> newOutputPaths = hasOutputPath ? new ArrayList<>() : null;
       for (int i : unCachedMeasurementIndexes) {
         IMeasurementSchema measurementSchema = node.getMeasurementSchema(i);
         unCachedPath.addMeasurement(measurementSchema.getMeasurementId(), 
measurementSchema);
+        if (hasOutputPath) {
+          newOutputPaths.add(node.getOutputPaths().get(i));
+        }
       }
       return createAlignedUpdateLastCacheOperator(
-          node.getOutputViewPath(),
+          newOutputPaths,
           node.getPlanNodeId(),
           unCachedPath,
           context,
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java
index b8fefee7201..5a03015d781 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java
@@ -68,7 +68,6 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.IntoPathDescr
 
 import org.apache.commons.lang3.Validate;
 import org.apache.tsfile.utils.Pair;
-import org.eclipse.jetty.util.StringUtil;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -520,8 +519,10 @@ public class PlanGraphPrinter extends 
PlanVisitor<List<String>, PlanGraphPrinter
     boxValue.add(
         String.format(
             "Series: %s%s", node.getDevicePath().getIDeviceID(), 
node.getMeasurementSchemas()));
-    if (StringUtil.isNotBlank(node.getOutputViewPath())) {
-      boxValue.add(String.format("ViewPath: %s", node.getOutputViewPath()));
+
+    List<String> outputPaths = node.getOutputPaths();
+    if (outputPaths != null) {
+      boxValue.add(String.format("OutputPaths: %s", outputPaths));
     }
     boxValue.add(printRegion(node.getRegionReplicaSet()));
     return render(node, boxValue, context);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/last/LastQueryNode.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/last/LastQueryNode.java
index 4ce3f29750c..32af69eb476 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/last/LastQueryNode.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/last/LastQueryNode.java
@@ -89,7 +89,8 @@ public class LastQueryNode extends MultiChildProcessNode {
       PartialPath devicePath,
       boolean aligned,
       List<IMeasurementSchema> measurementSchemas,
-      String outputViewPath) {
+      List<String> outputPaths,
+      boolean isOutputPathForView) {
     List<Integer> idxList = new ArrayList<>(measurementSchemas.size());
     for (IMeasurementSchema measurementSchema : measurementSchemas) {
       int idx =
@@ -103,7 +104,13 @@ public class LastQueryNode extends MultiChildProcessNode {
     }
     LastQueryScanNode scanNode =
         new LastQueryScanNode(
-            id, devicePath, aligned, idxList, outputViewPath, 
globalMeasurementSchemaList);
+            id,
+            devicePath,
+            aligned,
+            idxList,
+            outputPaths,
+            isOutputPathForView,
+            globalMeasurementSchemaList);
     children.add(scanNode);
     return scanNode.ramBytesUsed();
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java
index a16aeb779f4..1969357a7ac 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java
@@ -34,7 +34,6 @@ import com.google.common.collect.ImmutableList;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
 import org.apache.tsfile.write.schema.IMeasurementSchema;
-import org.eclipse.jetty.util.StringUtil;
 
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -63,7 +62,10 @@ public class LastQueryScanNode extends LastSeriesSourceNode {
   // It will be set when the current Node is added to the child by the upper 
LastQueryNode.
   private List<IMeasurementSchema> globalMeasurementSchemaList;
 
-  private final String outputViewPath;
+  // Store alias of paths or viewPath in this field.
+  private final List<String> outputPaths;
+  // Indicate if there is viewPath stored in outputPaths.
+  private final boolean isOutputPathForView;
 
   // The id of DataRegion where the node will run
   private TRegionReplicaSet regionReplicaSet;
@@ -74,14 +76,16 @@ public class LastQueryScanNode extends LastSeriesSourceNode 
{
       PartialPath devicePath,
       boolean aligned,
       List<Integer> indexOfMeasurementSchemas,
-      String outputViewPath,
+      List<String> outputPaths,
+      boolean isOutputPathForView,
       List<IMeasurementSchema> globalMeasurementSchemaList) {
     super(id, new AtomicInteger(1));
     this.aligned = aligned;
     this.devicePath = devicePath;
     this.indexOfMeasurementSchemas = indexOfMeasurementSchemas;
-    this.outputViewPath = outputViewPath;
+    this.outputPaths = outputPaths;
     this.globalMeasurementSchemaList = globalMeasurementSchemaList;
+    this.isOutputPathForView = isOutputPathForView;
   }
 
   public LastQueryScanNode(
@@ -90,14 +94,16 @@ public class LastQueryScanNode extends LastSeriesSourceNode 
{
       boolean aligned,
       List<Integer> indexOfMeasurementSchemas,
       AtomicInteger dataNodeSeriesScanNum,
-      String outputViewPath) {
+      List<String> outputPaths,
+      boolean isOutputPathForView) {
     this(
         id,
         devicePath,
         aligned,
         indexOfMeasurementSchemas,
         dataNodeSeriesScanNum,
-        outputViewPath,
+        outputPaths,
+        isOutputPathForView,
         null);
   }
 
@@ -107,13 +113,15 @@ public class LastQueryScanNode extends 
LastSeriesSourceNode {
       boolean aligned,
       List<Integer> indexOfMeasurementSchemas,
       AtomicInteger dataNodeSeriesScanNum,
-      String outputViewPath,
+      List<String> outputPaths,
+      boolean isOutputPathForView,
       List<IMeasurementSchema> globalMeasurementSchemaList) {
     super(id, dataNodeSeriesScanNum);
     this.aligned = aligned;
     this.devicePath = devicePath;
     this.indexOfMeasurementSchemas = indexOfMeasurementSchemas;
-    this.outputViewPath = outputViewPath;
+    this.outputPaths = outputPaths;
+    this.isOutputPathForView = isOutputPathForView;
     this.globalMeasurementSchemaList = globalMeasurementSchemaList;
   }
 
@@ -123,7 +131,8 @@ public class LastQueryScanNode extends LastSeriesSourceNode 
{
       boolean aligned,
       List<Integer> indexOfMeasurementSchemas,
       AtomicInteger dataNodeSeriesScanNum,
-      String outputViewPath,
+      List<String> outputPaths,
+      boolean isOutputPathForView,
       TRegionReplicaSet regionReplicaSet,
       boolean deviceInMultiRegion,
       List<IMeasurementSchema> globalMeasurementSchemaList) {
@@ -131,7 +140,8 @@ public class LastQueryScanNode extends LastSeriesSourceNode 
{
     this.devicePath = devicePath;
     this.aligned = aligned;
     this.indexOfMeasurementSchemas = indexOfMeasurementSchemas;
-    this.outputViewPath = outputViewPath;
+    this.outputPaths = outputPaths;
+    this.isOutputPathForView = isOutputPathForView;
     this.regionReplicaSet = regionReplicaSet;
     this.deviceInMultiRegion = deviceInMultiRegion;
     this.globalMeasurementSchemaList = globalMeasurementSchemaList;
@@ -158,14 +168,21 @@ public class LastQueryScanNode extends 
LastSeriesSourceNode {
     return this.aligned;
   }
 
-  public String getOutputViewPath() {
-    return outputViewPath;
+  public List<String> getOutputPaths() {
+    return outputPaths;
+  }
+
+  public boolean isOutputPathForView() {
+    return isOutputPathForView;
   }
 
   public String getOutputSymbolForSort() {
-    if (outputViewPath != null) {
-      return outputViewPath;
+    if (outputPaths != null && outputPaths.size() == 1) {
+      return outputPaths.get(0);
     }
+    // If outputPaths is null or size > 1, it means there is no view and no 
alias, just return the
+    // device name is ok,
+    // because the measurements have been sorted in AnalyzeVisitor if needed.
     return devicePath.toString();
   }
 
@@ -195,7 +212,8 @@ public class LastQueryScanNode extends LastSeriesSourceNode 
{
         aligned,
         indexOfMeasurementSchemas,
         getDataNodeSeriesScanNum(),
-        outputViewPath,
+        outputPaths,
+        isOutputPathForView,
         regionReplicaSet,
         deviceInMultiRegion,
         globalMeasurementSchemaList);
@@ -225,7 +243,7 @@ public class LastQueryScanNode extends LastSeriesSourceNode 
{
     return Objects.equals(devicePath, that.devicePath)
         && Objects.equals(aligned, that.aligned)
         && Objects.equals(indexOfMeasurementSchemas, 
that.indexOfMeasurementSchemas)
-        && Objects.equals(outputViewPath, that.outputViewPath)
+        && Objects.equals(outputPaths, that.outputPaths)
         && Objects.equals(regionReplicaSet, that.regionReplicaSet);
   }
 
@@ -236,20 +254,21 @@ public class LastQueryScanNode extends 
LastSeriesSourceNode {
         devicePath,
         aligned,
         indexOfMeasurementSchemas,
-        outputViewPath,
+        outputPaths,
         regionReplicaSet);
   }
 
   @Override
   public String toString() {
-    if (StringUtil.isNotBlank(outputViewPath)) {
+
+    if (outputPaths != null) {
       return String.format(
-          "LastQueryScanNode-%s:[Device: %s, Aligned: %s, Measurements: %s, 
ViewPath: %s, DataRegion: %s]",
+          "LastQueryScanNode-%s:[Device: %s, Aligned: %s, Measurements: %s, 
OutputPaths: %s, DataRegion: %s]",
           this.getPlanNodeId(),
           this.getDevicePath(),
           this.aligned,
           this.getMeasurementSchemas(),
-          this.getOutputViewPath(),
+          this.getOutputPaths(),
           PlanNodeUtil.printRegionReplicaSet(getRegionReplicaSet()));
     } else {
       return String.format(
@@ -272,10 +291,15 @@ public class LastQueryScanNode extends 
LastSeriesSourceNode {
       ReadWriteIOUtils.write(measurementSchema, byteBuffer);
     }
     ReadWriteIOUtils.write(getDataNodeSeriesScanNum().get(), byteBuffer);
-    ReadWriteIOUtils.write(outputViewPath == null, byteBuffer);
-    if (outputViewPath != null) {
-      ReadWriteIOUtils.write(outputViewPath, byteBuffer);
+    ReadWriteIOUtils.write(outputPaths == null, byteBuffer);
+    if (outputPaths != null) {
+      int size = outputPaths.size();
+      ReadWriteIOUtils.write(size, byteBuffer);
+      for (int i = 0; i < size; i++) {
+        ReadWriteIOUtils.write(outputPaths.get(i), byteBuffer);
+      }
     }
+    ReadWriteIOUtils.write(isOutputPathForView, byteBuffer);
     ReadWriteIOUtils.write(deviceInMultiRegion, byteBuffer);
   }
 
@@ -289,10 +313,15 @@ public class LastQueryScanNode extends 
LastSeriesSourceNode {
       ReadWriteIOUtils.write(measurementSchema, stream);
     }
     ReadWriteIOUtils.write(getDataNodeSeriesScanNum().get(), stream);
-    ReadWriteIOUtils.write(outputViewPath == null, stream);
-    if (outputViewPath != null) {
-      ReadWriteIOUtils.write(outputViewPath, stream);
+    ReadWriteIOUtils.write(outputPaths == null, stream);
+    if (outputPaths != null) {
+      int size = outputPaths.size();
+      ReadWriteIOUtils.write(size, stream);
+      for (int i = 0; i < size; i++) {
+        ReadWriteIOUtils.write(outputPaths.get(i), stream);
+      }
     }
+    ReadWriteIOUtils.write(isOutputPathForView, stream);
     ReadWriteIOUtils.write(deviceInMultiRegion, stream);
   }
 
@@ -307,7 +336,16 @@ public class LastQueryScanNode extends 
LastSeriesSourceNode {
 
     int dataNodeSeriesScanNum = ReadWriteIOUtils.readInt(byteBuffer);
     boolean isNull = ReadWriteIOUtils.readBool(byteBuffer);
-    String outputPathSymbol = isNull ? null : 
ReadWriteIOUtils.readString(byteBuffer);
+    List<String> outputPaths = null;
+    if (!isNull) {
+      int size = ReadWriteIOUtils.readInt(byteBuffer);
+      outputPaths = new ArrayList<>(size);
+      while (size > 0) {
+        outputPaths.add(ReadWriteIOUtils.readString(byteBuffer));
+        size--;
+      }
+    }
+    boolean isOutputPathForView = ReadWriteIOUtils.readBool(byteBuffer);
     boolean deviceInMultiRegion = ReadWriteIOUtils.readBool(byteBuffer);
     PlanNodeId planNodeId = PlanNodeId.deserialize(byteBuffer);
     return new LastQueryScanNode(
@@ -316,7 +354,8 @@ public class LastQueryScanNode extends LastSeriesSourceNode 
{
         aligned,
         measurementSchemas,
         new AtomicInteger(dataNodeSeriesScanNum),
-        outputPathSymbol,
+        outputPaths,
+        isOutputPathForView,
         null,
         deviceInMultiRegion,
         null);
@@ -369,6 +408,8 @@ public class LastQueryScanNode extends LastSeriesSourceNode 
{
         // The memory of each String has been calculated before
         + 
MemoryEstimationHelper.getEstimatedSizeOfCopiedPartialPath(devicePath)
         + 
MemoryEstimationHelper.getEstimatedSizeOfIntegerArrayList(indexOfMeasurementSchemas)
-        + RamUsageEstimator.sizeOf(outputViewPath);
+        + (outputPaths == null
+            ? 0L
+            : outputPaths.stream().mapToLong(path -> 
RamUsageEstimator.sizeOf(path)).sum());
   }
 }
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/LastQueryTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/LastQueryTest.java
index 2a30129bbd5..930ba4bcc7d 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/LastQueryTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/LastQueryTest.java
@@ -59,13 +59,15 @@ public class LastQueryTest {
             new MeasurementSchema("s3", TSDataType.INT32),
             new MeasurementSchema("s1", TSDataType.BOOLEAN),
             new MeasurementSchema("s2", TSDataType.INT32)),
-        null);
+        null,
+        false);
     lastQueryNode.addDeviceLastQueryScanNode(
         new PlanNodeId("test_last_query_scan2"),
         new PartialPath("root.test.d0"),
         false,
         Collections.singletonList(new MeasurementSchema("s0", 
TSDataType.BOOLEAN)),
-        null);
+        null,
+        false);
 
     Analysis analysis = Util.constructAnalysis();
     SourceRewriter sourceRewriter = new SourceRewriter(analysis);
@@ -255,7 +257,8 @@ public class LastQueryTest {
           devicePath,
           selectPath.isUnderAlignedEntity(),
           Collections.singletonList(selectPath.getMeasurementSchema()),
-          null);
+          null,
+          false);
     }
 
     return new LogicalQueryPlan(context, root);
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/DataQueryLogicalPlannerTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/DataQueryLogicalPlannerTest.java
index 2e51898a1f4..3ca25ab5ff8 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/DataQueryLogicalPlannerTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/DataQueryLogicalPlannerTest.java
@@ -88,7 +88,8 @@ public class DataQueryLogicalPlannerTest {
         d1s1Path.getDevicePath(),
         d1s1Path.isUnderAlignedEntity(),
         measurementSchemas,
-        null);
+        null,
+        false);
 
     measurementSchemas =
         Arrays.asList(
@@ -101,11 +102,12 @@ public class DataQueryLogicalPlannerTest {
         d2s1Path.getDevicePath(),
         d2s1Path.isUnderAlignedEntity(),
         measurementSchemas,
-        null);
+        null,
+        false);
 
     AlignedPath aPath = (AlignedPath) schemaMap.get("root.sg.d2.a");
     lastQueryNode.addDeviceLastQueryScanNode(
-        queryId.genPlanNodeId(), aPath.getDevicePath(), true, 
aPath.getSchemaList(), null);
+        queryId.genPlanNodeId(), aPath.getDevicePath(), true, 
aPath.getSchemaList(), null, false);
 
     PlanNode actualPlan = parseSQLToPlanNode(sql);
     Assert.assertEquals(actualPlan, lastQueryNode);
@@ -131,7 +133,8 @@ public class DataQueryLogicalPlannerTest {
         s3Path.getDevicePath(),
         s3Path.isUnderAlignedEntity(),
         measurementSchemas,
-        null);
+        null,
+        false);
 
     SortNode sortNode =
         new SortNode(
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/source/LastQueryScanNodeSerdeTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/source/LastQueryScanNodeSerdeTest.java
index 270411f3f51..3a0d8952e2d 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/source/LastQueryScanNodeSerdeTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/source/LastQueryScanNodeSerdeTest.java
@@ -44,6 +44,7 @@ public class LastQueryScanNodeSerdeTest {
             true,
             Arrays.asList(0, 1),
             null,
+            false,
             Arrays.asList(
                 new MeasurementSchema("s1", TSDataType.INT32),
                 new MeasurementSchema("s0", TSDataType.BOOLEAN)));
@@ -59,6 +60,7 @@ public class LastQueryScanNodeSerdeTest {
             false,
             Arrays.asList(0, 1),
             null,
+            false,
             Arrays.asList(
                 new MeasurementSchema("s1", TSDataType.INT32),
                 new MeasurementSchema("s0", TSDataType.BOOLEAN)));

Reply via email to