You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2022/04/14 04:17:58 UTC

[iotdb] branch master updated: Generate dataset header for query result set —— simple raw data query (#5522)

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

jackietien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new a0b1f19a88 Generate dataset header for query result set —— simple raw data query (#5522)
a0b1f19a88 is described below

commit a0b1f19a88651a2748a267db374e170b80c6b782
Author: liuminghui233 <36...@users.noreply.github.com>
AuthorDate: Thu Apr 14 12:17:53 2022 +0800

    Generate dataset header for query result set —— simple raw data query (#5522)
---
 .../iotdb/db/mpp/common/GroupByTimeParameter.java  | 27 -------
 .../plan/node => common/header}/ColumnHeader.java  | 23 +++++-
 .../iotdb/db/mpp/common/header/DatasetHeader.java  | 86 ++++++++++++++++++++++
 .../apache/iotdb/db/mpp/sql/analyze/Analysis.java  | 14 ++++
 .../apache/iotdb/db/mpp/sql/analyze/Analyzer.java  |  1 +
 .../iotdb/db/mpp/sql/planner/LogicalPlanner.java   |  9 ++-
 .../db/mpp/sql/planner/plan/IOutputPlanNode.java   |  2 +-
 .../db/mpp/sql/planner/plan/node/PlanNode.java     |  3 +-
 .../planner/plan/node/process/AggregateNode.java   |  8 +-
 .../planner/plan/node/process/DeviceMergeNode.java |  2 +-
 .../sql/planner/plan/node/process/FillNode.java    |  2 +-
 .../sql/planner/plan/node/process/FilterNode.java  |  2 +-
 .../planner/plan/node/process/FilterNullNode.java  |  2 +-
 .../plan/node/process/GroupByLevelNode.java        |  2 +-
 .../sql/planner/plan/node/process/LimitNode.java   |  2 +-
 .../sql/planner/plan/node/process/OffsetNode.java  |  2 +-
 .../sql/planner/plan/node/process/SortNode.java    |  2 +-
 .../planner/plan/node/process/TimeJoinNode.java    |  3 +-
 .../plan/node/source/SeriesAggregateScanNode.java  |  8 +-
 .../planner/plan/node/source/SeriesScanNode.java   |  2 +-
 .../db/mpp/sql/rewriter/WildcardsRemover.java      | 14 +---
 .../statement/component/GroupByLevelComponent.java |  2 +-
 .../component/GroupByLevelController.java          |  2 +-
 .../mpp/sql/statement/component/ResultColumn.java  |  9 +++
 .../sql/statement/component/SelectComponent.java   | 29 ++++++--
 .../statement/crud/AggregationQueryStatement.java  | 13 +++-
 .../sql/statement/crud/GroupByQueryStatement.java  | 11 +++
 .../mpp/sql/statement/crud/LastQueryStatement.java | 11 +++
 .../db/mpp/sql/statement/crud/QueryStatement.java  | 85 ++++++++++++++++-----
 .../db/mpp/sql/plan/QueryLogicalPlanUtil.java      |  2 +-
 .../node/process/GroupByLevelNodeSerdeTest.java    |  2 +-
 .../sql/plan/node/process/LimitNodeSerdeTest.java  |  2 +-
 .../sql/plan/node/process/OffsetNodeSerdeTest.java |  2 +-
 .../sql/plan/node/process/SortNodeSerdeTest.java   |  2 +-
 .../plan/node/process/TimeJoinNodeSerdeTest.java   |  2 +-
 35 files changed, 289 insertions(+), 101 deletions(-)

diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/common/GroupByTimeParameter.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/GroupByTimeParameter.java
deleted file mode 100644
index 33682741cd..0000000000
--- a/server/src/main/java/org/apache/iotdb/db/mpp/common/GroupByTimeParameter.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.mpp.common;
-
-import org.apache.iotdb.db.qp.physical.crud.GroupByTimePlan;
-
-/**
- * In single-node IoTDB, the GroupByTimePlan is used to represent the parameter of `group by time`.
- * To avoid ambiguity, we use another name `GroupByTimeParameter` here
- */
-public class GroupByTimeParameter extends GroupByTimePlan {}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/ColumnHeader.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/ColumnHeader.java
similarity index 87%
rename from server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/ColumnHeader.java
rename to server/src/main/java/org/apache/iotdb/db/mpp/common/header/ColumnHeader.java
index 12ca5f06fc..d6271c1cbe 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/ColumnHeader.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/ColumnHeader.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.iotdb.db.mpp.sql.planner.plan.node;
+package org.apache.iotdb.db.mpp.common.header;
 
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.PartialPath;
@@ -29,10 +29,19 @@ import java.util.Objects;
 
 public class ColumnHeader {
 
-  private final String pathName;
+  private String pathName;
   private String functionName;
   private final TSDataType dataType;
 
+  private String columnName;
+  private String alias;
+
+  public ColumnHeader(String columnName, TSDataType dataType, String alias) {
+    this.columnName = columnName;
+    this.dataType = dataType;
+    this.alias = alias;
+  }
+
   public ColumnHeader(String pathName, TSDataType dataType) {
     this.pathName = pathName;
     this.dataType = dataType;
@@ -45,6 +54,12 @@ public class ColumnHeader {
   }
 
   public String getColumnName() {
+    if (alias != null) {
+      return alias;
+    }
+    if (columnName != null) {
+      return columnName;
+    }
     if (functionName != null) {
       return String.format("%s(%s)", functionName, pathName);
     }
@@ -68,6 +83,10 @@ public class ColumnHeader {
     return new ColumnHeader(measurement, dataType);
   }
 
+  public boolean hasAlias() {
+    return alias != null;
+  }
+
   public void serialize(ByteBuffer byteBuffer) {
     ReadWriteIOUtils.write(pathName, byteBuffer);
     ReadWriteIOUtils.write(functionName, byteBuffer);
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/common/header/DatasetHeader.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/DatasetHeader.java
new file mode 100644
index 0000000000..32a236feae
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/DatasetHeader.java
@@ -0,0 +1,86 @@
+/*
+ * 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.mpp.common.header;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+
+import com.google.common.primitives.Bytes;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/** The header of query result dataset. */
+public class DatasetHeader {
+
+  // column names, data types and aliases of result dataset
+  private final List<ColumnHeader> columnHeaders;
+
+  // indicate whether the result dataset contain timestamp column
+  private final boolean isIgnoreTimestamp;
+
+  // map from
+  private Map<String, Integer> columnToTsBlockIndexMap;
+
+  public DatasetHeader(List<ColumnHeader> columnHeaders, boolean isIgnoreTimestamp) {
+    this.columnHeaders = columnHeaders;
+    this.isIgnoreTimestamp = isIgnoreTimestamp;
+  }
+
+  public List<ColumnHeader> getColumnHeaders() {
+    return columnHeaders;
+  }
+
+  public boolean isIgnoreTimestamp() {
+    return isIgnoreTimestamp;
+  }
+
+  public Map<String, Integer> getColumnToTsBlockIndexMap() {
+    return columnToTsBlockIndexMap;
+  }
+
+  public void setColumnToTsBlockIndexMap(List<String> outputColumnNames) {
+    this.columnToTsBlockIndexMap = new HashMap<>();
+    for (int i = 0; i < outputColumnNames.size(); i++) {
+      columnToTsBlockIndexMap.put(outputColumnNames.get(i), i);
+    }
+  }
+
+  public List<String> getRespColumns() {
+    return columnHeaders.stream().map(ColumnHeader::getColumnName).collect(Collectors.toList());
+  }
+
+  public List<TSDataType> getRespDataTypeList() {
+    return columnHeaders.stream().map(ColumnHeader::getColumnType).collect(Collectors.toList());
+  }
+
+  public List<Byte> getRespAliasColumns() {
+    BitSet aliasMap = new BitSet();
+    for (int i = 0; i < columnHeaders.size(); ++i) {
+      if (columnHeaders.get(i).hasAlias()) {
+        aliasMap.set(i);
+      }
+    }
+    return new ArrayList<>(Bytes.asList(aliasMap.toByteArray()));
+  }
+
+  public Map<String, Integer> getColumnNameIndexMap() {
+    return columnToTsBlockIndexMap;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/analyze/Analysis.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/analyze/Analysis.java
index 3a7b961bf4..a9e945ab4a 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/analyze/Analysis.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/analyze/Analysis.java
@@ -23,6 +23,7 @@ import org.apache.iotdb.commons.partition.DataPartition;
 import org.apache.iotdb.commons.partition.RegionReplicaSet;
 import org.apache.iotdb.commons.partition.SchemaPartition;
 import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.common.header.DatasetHeader;
 import org.apache.iotdb.db.mpp.common.schematree.SchemaTree;
 import org.apache.iotdb.db.mpp.sql.statement.Statement;
 import org.apache.iotdb.tsfile.read.expression.IExpression;
@@ -52,6 +53,11 @@ public class Analysis {
 
   private IExpression queryFilter;
 
+  // header of result dataset
+  private DatasetHeader respDatasetHeader;
+
+  public Analysis() {}
+
   public List<RegionReplicaSet> getPartitionInfo(PartialPath seriesPath, Filter timefilter) {
     // TODO: (xingtanzjr) implement the calculation of timePartitionIdList
     return dataPartition.getDataRegionReplicaSet(seriesPath.getDevice(), null);
@@ -96,4 +102,12 @@ public class Analysis {
   public void setQueryFilter(IExpression expression) {
     this.queryFilter = expression;
   }
+
+  public DatasetHeader getRespDatasetHeader() {
+    return respDatasetHeader;
+  }
+
+  public void setRespDatasetHeader(DatasetHeader respDatasetHeader) {
+    this.respDatasetHeader = respDatasetHeader;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/analyze/Analyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/analyze/Analyzer.java
index 72b7104285..5eca107403 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/analyze/Analyzer.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/analyze/Analyzer.java
@@ -160,6 +160,7 @@ public class Analyzer {
         }
         analysis.setStatement(rewrittenStatement);
         analysis.setSchemaTree(schemaTree);
+        analysis.setRespDatasetHeader(queryStatement.constructDatasetHeader());
         analysis.setDataPartitionInfo(dataPartition);
       } catch (StatementAnalyzeException
           | PathNumOverLimitException
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/LogicalPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/LogicalPlanner.java
index 8f6dba3cfe..dbedde3e33 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/LogicalPlanner.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/LogicalPlanner.java
@@ -24,6 +24,7 @@ import org.apache.iotdb.db.mpp.common.MPPQueryContext;
 import org.apache.iotdb.db.mpp.common.schematree.DeviceSchemaInfo;
 import org.apache.iotdb.db.mpp.sql.analyze.Analysis;
 import org.apache.iotdb.db.mpp.sql.optimization.PlanOptimizer;
+import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.LogicalQueryPlan;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.metedata.write.AlterTimeSeriesNode;
@@ -84,6 +85,10 @@ public class LogicalPlanner {
       for (PlanOptimizer optimizer : optimizers) {
         rootNode = optimizer.optimize(rootNode, context);
       }
+
+      analysis
+          .getRespDatasetHeader()
+          .setColumnToTsBlockIndexMap(((IOutputPlanNode) rootNode).getOutputColumnNames());
     }
 
     return new LogicalQueryPlan(context, rootNode);
@@ -106,7 +111,7 @@ public class LogicalPlanner {
       QueryPlanBuilder planBuilder = new QueryPlanBuilder(context);
 
       planBuilder.planRawDataQuerySource(
-          queryStatement.getDeviceNameToPathsMap(),
+          queryStatement.getDeviceNameToDeduplicatedPathsMap(),
           queryStatement.getResultOrder(),
           queryStatement.isAlignByDevice(),
           analysis.getQueryFilter(),
@@ -129,7 +134,7 @@ public class LogicalPlanner {
         // with value filter
         planBuilder.planAggregationSourceWithValueFilter(
             queryStatement.getDeviceNameToAggregationsMap(),
-            queryStatement.getDeviceNameToPathsMap(),
+            queryStatement.getDeviceNameToDeduplicatedPathsMap(),
             queryStatement.getResultOrder(),
             queryStatement.isAlignByDevice(),
             analysis.getQueryFilter(),
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/IOutputPlanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/IOutputPlanNode.java
index f88ad5d70d..661066a314 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/IOutputPlanNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/IOutputPlanNode.java
@@ -19,7 +19,7 @@
 
 package org.apache.iotdb.db.mpp.sql.planner.plan;
 
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 
 import java.util.List;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanNode.java
index d99c3361bf..f019999920 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanNode.java
@@ -28,8 +28,7 @@ import java.util.Objects;
 
 import static java.util.Objects.requireNonNull;
 
-/** The base class of query executable operators, which is used to compose logical query plan. */
-// TODO: consider how to restrict the children type for each type of ExecOperator
+/** The base class of query logical plan nodes, which is used to compose logical query plan. */
 public abstract class PlanNode {
   protected static final int NO_CHILD_ALLOWED = 0;
   protected static final int ONE_CHILD = 1;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/AggregateNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/AggregateNode.java
index ebd55c3f81..d5d018f0ee 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/AggregateNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/AggregateNode.java
@@ -20,13 +20,13 @@ package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.db.metadata.path.PartialPath;
 import org.apache.iotdb.db.metadata.path.PathDeserializeUtil;
-import org.apache.iotdb.db.mpp.common.GroupByTimeParameter;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanVisitor;
+import org.apache.iotdb.db.mpp.sql.statement.component.GroupByTimeComponent;
 import org.apache.iotdb.db.query.aggregation.AggregationType;
 import org.apache.iotdb.tsfile.exception.NotImplementedException;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
@@ -62,7 +62,7 @@ public class AggregateNode extends ProcessNode implements IOutputPlanNode {
 
   // The parameter of `group by time`.
   // Its value will be null if there is no `group by time` clause.
-  private final GroupByTimeParameter groupByTimeParameter;
+  private final GroupByTimeComponent groupByTimeParameter;
 
   private final List<ColumnHeader> columnHeaders = new ArrayList<>();
 
@@ -72,7 +72,7 @@ public class AggregateNode extends ProcessNode implements IOutputPlanNode {
       PlanNodeId id,
       PlanNode child,
       Map<PartialPath, Set<AggregationType>> aggregateFuncMap,
-      GroupByTimeParameter groupByTimeParameter) {
+      GroupByTimeComponent groupByTimeParameter) {
     super(id);
     this.child = child;
     this.aggregateFuncMap = aggregateFuncMap;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/DeviceMergeNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/DeviceMergeNode.java
index d427b85809..83514408ac 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/DeviceMergeNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/DeviceMergeNode.java
@@ -19,9 +19,9 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.PlanFragment;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FillNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FillNode.java
index ed6811c4c4..b0ccf48018 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FillNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FillNode.java
@@ -19,8 +19,8 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FilterNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FilterNode.java
index 08199dc0ef..25a117cb6f 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FilterNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FilterNode.java
@@ -19,8 +19,8 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FilterNullNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FilterNullNode.java
index ee1197cd57..ecde0204d4 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FilterNullNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/FilterNullNode.java
@@ -19,8 +19,8 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/GroupByLevelNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/GroupByLevelNode.java
index a2d2f63e6f..dc06dec753 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/GroupByLevelNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/GroupByLevelNode.java
@@ -19,8 +19,8 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/LimitNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/LimitNode.java
index b784039af3..ae0291edb1 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/LimitNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/LimitNode.java
@@ -19,8 +19,8 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/OffsetNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/OffsetNode.java
index 2ec842aebe..15544e4f64 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/OffsetNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/OffsetNode.java
@@ -19,8 +19,8 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/SortNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/SortNode.java
index 59b9df6e82..c25262c723 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/SortNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/SortNode.java
@@ -19,8 +19,8 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/TimeJoinNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/TimeJoinNode.java
index 118d98d6c1..90ebc81ee3 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/TimeJoinNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/process/TimeJoinNode.java
@@ -19,8 +19,8 @@
 package org.apache.iotdb.db.mpp.sql.planner.plan.node.process;
 
 import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
@@ -101,6 +101,7 @@ public class TimeJoinNode extends ProcessNode implements IOutputPlanNode {
     return columnHeaders.stream().map(ColumnHeader::getColumnName).collect(Collectors.toList());
   }
 
+  @Override
   public List<TSDataType> getOutputColumnTypes() {
     return columnHeaders.stream().map(ColumnHeader::getColumnType).collect(Collectors.toList());
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/source/SeriesAggregateScanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/source/SeriesAggregateScanNode.java
index 87c2ff1d05..d54501cb51 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/source/SeriesAggregateScanNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/source/SeriesAggregateScanNode.java
@@ -22,13 +22,13 @@ import org.apache.iotdb.commons.partition.RegionReplicaSet;
 import org.apache.iotdb.commons.utils.TestOnly;
 import org.apache.iotdb.db.metadata.path.PartialPath;
 import org.apache.iotdb.db.metadata.path.PathDeserializeUtil;
-import org.apache.iotdb.db.mpp.common.GroupByTimeParameter;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanVisitor;
+import org.apache.iotdb.db.mpp.sql.statement.component.GroupByTimeComponent;
 import org.apache.iotdb.db.mpp.sql.statement.component.OrderBy;
 import org.apache.iotdb.db.query.aggregation.AggregationType;
 import org.apache.iotdb.tsfile.exception.NotImplementedException;
@@ -80,7 +80,7 @@ public class SeriesAggregateScanNode extends SourceNode implements IOutputPlanNo
 
   // The parameter of `group by time`
   // Its value will be null if there is no `group by time` clause,
-  private final GroupByTimeParameter groupByTimeParameter;
+  private final GroupByTimeComponent groupByTimeParameter;
 
   private List<ColumnHeader> columnHeaders;
 
@@ -93,7 +93,7 @@ public class SeriesAggregateScanNode extends SourceNode implements IOutputPlanNo
       List<AggregationType> aggregateFuncList,
       OrderBy scanOrder,
       Filter timeFilter,
-      GroupByTimeParameter groupByTimeParameter) {
+      GroupByTimeComponent groupByTimeParameter) {
     super(id);
     this.seriesPath = seriesPath;
     this.aggregateFuncList = aggregateFuncList;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/source/SeriesScanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/source/SeriesScanNode.java
index 9700720466..11726b62d9 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/source/SeriesScanNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/source/SeriesScanNode.java
@@ -22,8 +22,8 @@ import org.apache.iotdb.commons.partition.RegionReplicaSet;
 import org.apache.iotdb.commons.utils.TestOnly;
 import org.apache.iotdb.db.metadata.path.PartialPath;
 import org.apache.iotdb.db.metadata.path.PathDeserializeUtil;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.IOutputPlanNode;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeType;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/rewriter/WildcardsRemover.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/rewriter/WildcardsRemover.java
index f6c7fb327a..c740bdf650 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/rewriter/WildcardsRemover.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/rewriter/WildcardsRemover.java
@@ -55,12 +55,6 @@ public class WildcardsRemover {
 
   private ColumnPaginationController paginationController;
 
-  /**
-   * Since IoTDB v0.13, all DDL and DML use patternMatch as default. Before IoTDB v0.13, all DDL and
-   * DML use prefixMatch.
-   */
-  private boolean isPrefixMatch;
-
   public Statement rewrite(Statement statement, SchemaTree schemaTree)
       throws StatementAnalyzeException, PathNumOverLimitException {
     QueryStatement queryStatement = (QueryStatement) statement;
@@ -73,7 +67,6 @@ public class WildcardsRemover {
                 || queryStatement instanceof LastQueryStatement
                 || queryStatement.isGroupByLevel());
     this.schemaTree = schemaTree;
-    this.isPrefixMatch = queryStatement.isPrefixMatchPath();
 
     if (queryStatement.getIndexType() == null) {
       // remove wildcards in SELECT clause
@@ -271,10 +264,7 @@ public class WildcardsRemover {
     try {
       Pair<List<MeasurementPath>, Integer> pair =
           schemaTree.searchMeasurementPaths(
-              path,
-              paginationController.getCurLimit(),
-              paginationController.getCurOffset(),
-              isPrefixMatch);
+              path, paginationController.getCurLimit(), paginationController.getCurOffset(), false);
       paginationController.consume(pair.left.size(), pair.right);
       return pair.left;
     } catch (Exception e) {
@@ -329,7 +319,7 @@ public class WildcardsRemover {
     try {
       for (PartialPath originalPath : originalPaths) {
         List<MeasurementPath> all =
-            schemaTree.searchMeasurementPaths(originalPath, 0, 0, isPrefixMatch).left;
+            schemaTree.searchMeasurementPaths(originalPath, 0, 0, false).left;
         if (all.isEmpty()) {
           throw new StatementAnalyzeException(
               String.format("Unknown time series %s in `where clause`", originalPath));
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/GroupByLevelComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/GroupByLevelComponent.java
index 6d02efdee4..fc373d6059 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/GroupByLevelComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/GroupByLevelComponent.java
@@ -19,7 +19,7 @@
 
 package org.apache.iotdb.db.mpp.sql.statement.component;
 
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.statement.StatementNode;
 
 import java.util.Map;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/GroupByLevelController.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/GroupByLevelController.java
index aec0d007ba..151c98196c 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/GroupByLevelController.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/GroupByLevelController.java
@@ -22,7 +22,7 @@ package org.apache.iotdb.db.mpp.sql.statement.component;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.db.exception.sql.StatementAnalyzeException;
 import org.apache.iotdb.db.metadata.path.PartialPath;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.statement.crud.AggregationQueryStatement;
 import org.apache.iotdb.db.mpp.sql.statement.crud.QueryStatement;
 import org.apache.iotdb.db.query.expression.Expression;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/ResultColumn.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/ResultColumn.java
index d7418523ee..a9cbcf0edd 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/ResultColumn.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/ResultColumn.java
@@ -21,10 +21,12 @@ package org.apache.iotdb.db.mpp.sql.statement.component;
 
 import org.apache.iotdb.db.exception.sql.StatementAnalyzeException;
 import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree;
 import org.apache.iotdb.db.mpp.sql.rewriter.WildcardsRemover;
 import org.apache.iotdb.db.mpp.sql.statement.StatementNode;
 import org.apache.iotdb.db.query.expression.Expression;
+import org.apache.iotdb.db.query.expression.unary.TimeSeriesOperand;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 
 import java.util.ArrayList;
@@ -171,6 +173,13 @@ public class ResultColumn extends StatementNode {
     }
   }
 
+  public ColumnHeader constructColumnHeader() {
+    return new ColumnHeader(
+        this.getExpressionString(),
+        ((TimeSeriesOperand) this.getExpression()).getPath().getSeriesType(),
+        this.getAlias());
+  }
+
   @Override
   public String toString() {
     return "ResultColumn{" + "expression=" + expression + ", alias='" + alias + '\'' + '}';
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/SelectComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/SelectComponent.java
index b81b3244e1..450abaae92 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/SelectComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/component/SelectComponent.java
@@ -49,7 +49,8 @@ public class SelectComponent extends StatementNode {
 
   private List<PartialPath> pathsCache;
   private List<String> aggregationFunctionsCache;
-  private Map<String, Set<PartialPath>> deviceIdToPathsCache;
+  private Map<String, List<PartialPath>> deviceNameToPathsCache;
+  private Map<String, Set<PartialPath>> deviceNameToDeduplicatedPathsCache;
 
   public SelectComponent(ZoneId zoneId) {
     this.zoneId = zoneId;
@@ -143,16 +144,32 @@ public class SelectComponent extends StatementNode {
     return aggregationFunctionsCache;
   }
 
-  public Map<String, Set<PartialPath>> getDeviceIdToPathsMap() {
-    if (deviceIdToPathsCache == null) {
-      deviceIdToPathsCache = new HashMap<>();
+  public Map<String, Set<PartialPath>> getDeviceNameToDeduplicatedPathsMap() {
+    if (deviceNameToDeduplicatedPathsCache == null) {
+      deviceNameToDeduplicatedPathsCache = new HashMap<>();
       for (ResultColumn resultColumn : resultColumns) {
         for (PartialPath path : resultColumn.collectPaths()) {
-          deviceIdToPathsCache.computeIfAbsent(path.getDevice(), k -> new HashSet<>()).add(path);
+          deviceNameToDeduplicatedPathsCache
+              .computeIfAbsent(path.getDevice(), k -> new HashSet<>())
+              .add(path);
         }
       }
     }
-    return deviceIdToPathsCache;
+    return deviceNameToDeduplicatedPathsCache;
+  }
+
+  public Map<String, List<PartialPath>> getDeviceNameToPathsMap() {
+    if (deviceNameToPathsCache == null) {
+      deviceNameToPathsCache = new HashMap<>();
+      for (ResultColumn resultColumn : resultColumns) {
+        for (PartialPath path : resultColumn.collectPaths()) {
+          deviceNameToPathsCache
+              .computeIfAbsent(path.getDevice(), k -> new ArrayList<>())
+              .add(path);
+        }
+      }
+    }
+    return deviceNameToPathsCache;
   }
 
   public List<Path> getDeduplicatedPaths() {
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/AggregationQueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/AggregationQueryStatement.java
index 1532c07a46..41729d0d97 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/AggregationQueryStatement.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/AggregationQueryStatement.java
@@ -21,6 +21,8 @@ package org.apache.iotdb.db.mpp.sql.statement.crud;
 
 import org.apache.iotdb.db.exception.sql.SemanticException;
 import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
+import org.apache.iotdb.db.mpp.common.header.DatasetHeader;
 import org.apache.iotdb.db.mpp.sql.statement.StatementVisitor;
 import org.apache.iotdb.db.mpp.sql.statement.component.GroupByLevelComponent;
 import org.apache.iotdb.db.mpp.sql.statement.component.ResultColumn;
@@ -30,10 +32,7 @@ import org.apache.iotdb.db.query.expression.Expression;
 import org.apache.iotdb.db.query.expression.unary.FunctionExpression;
 import org.apache.iotdb.db.query.expression.unary.TimeSeriesOperand;
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 public class AggregationQueryStatement extends QueryStatement {
 
@@ -76,6 +75,12 @@ public class AggregationQueryStatement extends QueryStatement {
     return deviceNameToAggregationsMap;
   }
 
+  public DatasetHeader constructDatasetHeader() {
+    List<ColumnHeader> columnHeaders = new ArrayList<>();
+    // TODO: consider Aggregation
+    return new DatasetHeader(columnHeaders, true);
+  }
+
   @Override
   public void selfCheck() {
     super.selfCheck();
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/GroupByQueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/GroupByQueryStatement.java
index 73191009d1..5a3fbc937b 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/GroupByQueryStatement.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/GroupByQueryStatement.java
@@ -19,9 +19,14 @@
 
 package org.apache.iotdb.db.mpp.sql.statement.crud;
 
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
+import org.apache.iotdb.db.mpp.common.header.DatasetHeader;
 import org.apache.iotdb.db.mpp.sql.statement.StatementVisitor;
 import org.apache.iotdb.db.mpp.sql.statement.component.GroupByTimeComponent;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class GroupByQueryStatement extends AggregationQueryStatement {
 
   protected GroupByTimeComponent groupByTimeComponent;
@@ -42,6 +47,12 @@ public class GroupByQueryStatement extends AggregationQueryStatement {
     this.groupByTimeComponent = groupByTimeComponent;
   }
 
+  public DatasetHeader constructDatasetHeader() {
+    List<ColumnHeader> columnHeaders = new ArrayList<>();
+    // TODO: consider GROUP BY
+    return new DatasetHeader(columnHeaders, false);
+  }
+
   public <R, C> R accept(StatementVisitor<R, C> visitor, C context) {
     return visitor.visitGroupByQuery(this, context);
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/LastQueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/LastQueryStatement.java
index 0faa99a2c3..2cf8235889 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/LastQueryStatement.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/LastQueryStatement.java
@@ -20,11 +20,16 @@
 package org.apache.iotdb.db.mpp.sql.statement.crud;
 
 import org.apache.iotdb.db.exception.sql.SemanticException;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
+import org.apache.iotdb.db.mpp.common.header.DatasetHeader;
 import org.apache.iotdb.db.mpp.sql.statement.StatementVisitor;
 import org.apache.iotdb.db.mpp.sql.statement.component.ResultColumn;
 import org.apache.iotdb.db.query.expression.Expression;
 import org.apache.iotdb.db.query.expression.unary.TimeSeriesOperand;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class LastQueryStatement extends QueryStatement {
 
   public LastQueryStatement() {
@@ -35,6 +40,12 @@ public class LastQueryStatement extends QueryStatement {
     super(queryStatement);
   }
 
+  public DatasetHeader constructDatasetHeader() {
+    List<ColumnHeader> columnHeaders = new ArrayList<>();
+    // TODO: consider LAST
+    return new DatasetHeader(columnHeaders, false);
+  }
+
   @Override
   public void selfCheck() {
     super.selfCheck();
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/QueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/QueryStatement.java
index 0dcc1a397e..850bdd21c6 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/QueryStatement.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/statement/crud/QueryStatement.java
@@ -22,11 +22,18 @@ package org.apache.iotdb.db.mpp.sql.statement.crud;
 import org.apache.iotdb.db.exception.sql.SemanticException;
 import org.apache.iotdb.db.index.common.IndexType;
 import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
+import org.apache.iotdb.db.mpp.common.header.DatasetHeader;
 import org.apache.iotdb.db.mpp.sql.constant.StatementType;
 import org.apache.iotdb.db.mpp.sql.statement.Statement;
 import org.apache.iotdb.db.mpp.sql.statement.StatementVisitor;
 import org.apache.iotdb.db.mpp.sql.statement.component.*;
 import org.apache.iotdb.db.qp.constant.SQLConstant;
+import org.apache.iotdb.db.qp.physical.crud.MeasurementInfo;
+import org.apache.iotdb.db.query.expression.Expression;
+import org.apache.iotdb.db.query.expression.unary.FunctionExpression;
+import org.apache.iotdb.db.query.expression.unary.TimeSeriesOperand;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -80,12 +87,6 @@ public class QueryStatement extends Statement {
   // TODO: add comments
   protected IndexType indexType;
 
-  /**
-   * Since IoTDB v0.13, all DDL and DML use patternMatch as default. Before IoTDB v0.13, all DDL and
-   * DML use prefixMatch.
-   */
-  protected boolean isPrefixMatchPath = false;
-
   public QueryStatement() {
     this.statementType = StatementType.QUERY;
   }
@@ -215,14 +216,6 @@ public class QueryStatement extends Statement {
     this.indexType = indexType;
   }
 
-  public boolean isPrefixMatchPath() {
-    return isPrefixMatchPath;
-  }
-
-  public void setPrefixMatchPath(boolean prefixMatchPath) {
-    isPrefixMatchPath = prefixMatchPath;
-  }
-
   public boolean isGroupByLevel() {
     return false;
   };
@@ -243,18 +236,20 @@ public class QueryStatement extends Statement {
     return selectComponent.isHasUserDefinedAggregationFunction();
   }
 
-  public Map<String, Set<PartialPath>> getDeviceNameToPathsMap() {
-    Map<String, Set<PartialPath>> deviceNameToPathsMap =
-        new HashMap<>(getSelectComponent().getDeviceIdToPathsMap());
+  public Map<String, Set<PartialPath>> getDeviceNameToDeduplicatedPathsMap() {
+    Map<String, Set<PartialPath>> deviceNameToDeduplicatedPathsMap =
+        new HashMap<>(getSelectComponent().getDeviceNameToDeduplicatedPathsMap());
     if (getWhereCondition() != null) {
       for (PartialPath path :
           getWhereCondition().getQueryFilter().getPathSet().stream()
               .filter(SQLConstant::isNotReservedPath)
               .collect(Collectors.toList())) {
-        deviceNameToPathsMap.computeIfAbsent(path.getDevice(), k -> new HashSet<>()).add(path);
+        deviceNameToDeduplicatedPathsMap
+            .computeIfAbsent(path.getDevice(), k -> new HashSet<>())
+            .add(path);
       }
     }
-    return deviceNameToPathsMap;
+    return deviceNameToDeduplicatedPathsMap;
   }
 
   public List<String> getSelectedPathNames() {
@@ -268,6 +263,47 @@ public class QueryStatement extends Statement {
     return new ArrayList<>(pathSet);
   }
 
+  public DatasetHeader constructDatasetHeader() {
+    List<ColumnHeader> columnHeaders = new ArrayList<>();
+    if (this.isAlignByDevice()) {
+      // add DEVICE column
+      columnHeaders.add(
+          new ColumnHeader(SQLConstant.ALIGNBY_DEVICE_COLUMN_NAME, TSDataType.TEXT, null));
+
+      // TODO: consider ALIGN BY DEVICE
+    } else {
+      columnHeaders.addAll(
+          this.getSelectComponent().getResultColumns().stream()
+              .map(ResultColumn::constructColumnHeader)
+              .collect(Collectors.toList()));
+    }
+    return new DatasetHeader(columnHeaders, false);
+  }
+
+  /**
+   * If path is a vectorPartialPath, we return its measurementId + subMeasurement as the final
+   * measurement. e.g. path: root.sg.d1.vector1[s1], return "vector1.s1".
+   */
+  private String getMeasurementName(PartialPath path, String aggregation) {
+    String initialMeasurement = path.getMeasurement();
+    if (aggregation != null) {
+      initialMeasurement = aggregation + "(" + initialMeasurement + ")";
+    }
+    return initialMeasurement;
+  }
+
+  private PartialPath getPathFromExpression(Expression expression) {
+    return expression instanceof TimeSeriesOperand
+        ? ((TimeSeriesOperand) expression).getPath()
+        : (((FunctionExpression) expression).getPaths().get(0));
+  }
+
+  private String getAggregationFromExpression(Expression expression) {
+    return expression.isBuiltInAggregationFunctionExpression()
+        ? ((FunctionExpression) expression).getFunctionName()
+        : null;
+  }
+
   /** semantic check */
   public void selfCheck() {
     if (isAlignByDevice()) {
@@ -284,6 +320,17 @@ public class QueryStatement extends Statement {
     }
   }
 
+  /**
+   * Check datatype consistency in ALIGN BY DEVICE.
+   *
+   * <p>an inconsistent example: select s0 from root.sg1.d1, root.sg1.d2 align by device, return
+   * false while root.sg1.d1.s0 is INT32 and root.sg1.d2.s0 is FLOAT.
+   */
+  private boolean checkDataTypeConsistency(
+      TSDataType checkedDataType, MeasurementInfo measurementInfo) {
+    return measurementInfo == null || checkedDataType.equals(measurementInfo.getColumnDataType());
+  }
+
   @Override
   public <R, C> R accept(StatementVisitor<R, C> visitor, C context) {
     return visitor.visitQuery(this, context);
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/QueryLogicalPlanUtil.java b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/QueryLogicalPlanUtil.java
index 97ab163c68..57792799ae 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/QueryLogicalPlanUtil.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/QueryLogicalPlanUtil.java
@@ -22,7 +22,7 @@ package org.apache.iotdb.db.mpp.sql.plan;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.metadata.path.PartialPath;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.process.AggregateNode;
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/GroupByLevelNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/GroupByLevelNodeSerdeTest.java
index ddddccc875..ada62da218 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/GroupByLevelNodeSerdeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/GroupByLevelNodeSerdeTest.java
@@ -21,8 +21,8 @@ package org.apache.iotdb.db.mpp.sql.plan.node.process;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.plan.node.PlanNodeDeserializeHelper;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.metedata.read.ShowDevicesNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.process.AggregateNode;
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/LimitNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/LimitNodeSerdeTest.java
index ac41167b04..47392bdb00 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/LimitNodeSerdeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/LimitNodeSerdeTest.java
@@ -21,8 +21,8 @@ package org.apache.iotdb.db.mpp.sql.plan.node.process;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.plan.node.PlanNodeDeserializeHelper;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.metedata.read.ShowDevicesNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.process.AggregateNode;
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/OffsetNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/OffsetNodeSerdeTest.java
index 0924bee3fd..5c6ecdd9cd 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/OffsetNodeSerdeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/OffsetNodeSerdeTest.java
@@ -25,9 +25,9 @@ import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.metadata.path.PartialPath;
 import org.apache.iotdb.db.mpp.common.filter.BasicFunctionFilter;
 import org.apache.iotdb.db.mpp.common.filter.QueryFilter;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.constant.FilterConstant.FilterType;
 import org.apache.iotdb.db.mpp.sql.plan.node.PlanNodeDeserializeHelper;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.metedata.read.ShowDevicesNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.process.AggregateNode;
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/SortNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/SortNodeSerdeTest.java
index 38cb2e7f07..58f397c7e0 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/SortNodeSerdeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/SortNodeSerdeTest.java
@@ -21,8 +21,8 @@ package org.apache.iotdb.db.mpp.sql.plan.node.process;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.plan.node.PlanNodeDeserializeHelper;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.metedata.read.ShowDevicesNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.process.AggregateNode;
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/TimeJoinNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/TimeJoinNodeSerdeTest.java
index c720974164..987c8a3f68 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/TimeJoinNodeSerdeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/node/process/TimeJoinNodeSerdeTest.java
@@ -21,8 +21,8 @@ package org.apache.iotdb.db.mpp.sql.plan.node.process;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.mpp.common.header.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.plan.node.PlanNodeDeserializeHelper;
-import org.apache.iotdb.db.mpp.sql.planner.plan.node.ColumnHeader;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.metedata.read.ShowDevicesNode;
 import org.apache.iotdb.db.mpp.sql.planner.plan.node.process.AggregateNode;