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 80cd9f70284 Perfect the aggregation queries when there is no devices 
or no data partitions.
80cd9f70284 is described below

commit 80cd9f702845464d4cb37f0b28667e97b3628378
Author: Beyyes <[email protected]>
AuthorDate: Fri Oct 25 09:47:04 2024 +0800

    Perfect the aggregation queries when there is no devices or no data 
partitions.
---
 .../db/it/IoTDBMultiIDsWithAttributesTableIT.java  |  43 +++++--
 .../TableAggregationTableScanOperator.java         |  35 ++++--
 .../TableModelStatementMemorySourceVisitor.java    |   6 +-
 .../plan/planner/OperatorTreeGenerator.java        |   2 +-
 .../plan/planner/plan/node/PlanGraphPrinter.java   |   2 +-
 .../plan/relational/analyzer/Analysis.java         |  17 ++-
 .../distribute/TableDistributedPlanGenerator.java  | 130 +++++++++------------
 .../PushAggregationIntoTableScan.java              |   2 +-
 .../optimizations/PushPredicateIntoTableScan.java  |  10 +-
 .../TransformAggregationToStreamable.java          |   2 +-
 10 files changed, 145 insertions(+), 104 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiIDsWithAttributesTableIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiIDsWithAttributesTableIT.java
index fa1a07f305e..34b8af428a3 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiIDsWithAttributesTableIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiIDsWithAttributesTableIT.java
@@ -910,14 +910,15 @@ public class IoTDBMultiIDsWithAttributesTableIT {
         };
 
     // TODO(beyyes) test below
-    //    sql = "select count(*) from (\n" +
-    //            "\tselect device, level, date_bin(1d, time) as bin, \n" +
-    //            "\tcount(num) as count_num, count(*) as count_star, 
count(device) as count_device,
-    // count(date) as count_date, count(attr1) as count_attr1, count(attr2) as 
count_attr2,
-    // count(time) as count_time, avg(num) as avg_num \n" +
-    //            "\tfrom table0 \n" +
-    //            "\tgroup by 3, device, level order by device, level, bin\n" +
-    //            ")\n";
+    //        sql = "select count(*) from (\n" +
+    //                "\tselect device, level, date_bin(1d, time) as bin, \n" +
+    //                "\tcount(num) as count_num, count(*) as count_star, 
count(device) as
+    // count_device,
+    //     count(date) as count_date, count(attr1) as count_attr1, 
count(attr2) as count_attr2,
+    //     count(time) as count_time, avg(num) as avg_num \n" +
+    //                "\tfrom table0 \n" +
+    //                "\tgroup by 3, device, level order by device, level, 
bin\n" +
+    //                ")\n";
   }
 
   @Test
@@ -1015,6 +1016,27 @@ public class IoTDBMultiIDsWithAttributesTableIT {
             + "count(attr1) as count_attr1, count(attr2) as count_attr2, 
count(time) as count_time, sum(num) as sum_num,"
             + "avg(num) as avg_num from table0 where time=32 or 
time=1971-04-27T01:46:40.000+08:00 group by 3, device, level order by device, 
level";
     tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE_NAME);
+
+    // queried device is not exist
+    expectedHeader = buildHeaders(3);
+    sql = "select count(*), count(num), sum(num) from table0 where 
device='d_not_exist'";
+    retArray = new String[] {"0,0,null,"};
+    tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE_NAME);
+    sql =
+        "select count(*), count(num), sum(num) from table0 where 
device='d_not_exist1' or device='d_not_exist2'";
+    retArray = new String[] {"0,0,null,"};
+    tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE_NAME);
+
+    // no data in given time range
+    sql = "select count(*), count(num), sum(num) from table0 where 
time>2100-04-26T18:01:40.000";
+    retArray = new String[] {"0,0,null,"};
+    tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE_NAME);
+
+    // only one device has data in queried time
+    expectedHeader = buildHeaders(2);
+    sql = "select count(num),sum(num) from table1 where time=0";
+    retArray = new String[] {"2,6.0,"};
+    tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE_NAME);
   }
 
   @Test
@@ -1161,6 +1183,11 @@ public class IoTDBMultiIDsWithAttributesTableIT {
           
"1970-01-01T00:00:00.100Z,1970-01-01T00:00:00.100Z,1970-01-01T00:00:00.100Z,1971-01-01T00:00:00.000Z,1971-01-01T00:00:00.000Z,1970-01-01T00:00:00.100Z,1970-01-01T00:00:00.100Z,1970-01-01T00:00:00.100Z,1970-01-01T00:00:00.100Z,1970-01-01T00:00:00.100Z,1971-08-20T11:33:20.000Z,1971-01-01T00:01:40.000Z,1971-01-01T00:01:40.000Z,",
         };
     tableResultSetEqualTest(sql, expectedHeader1, retArray, DATABASE_NAME);
+
+    expectedHeader = buildHeaders(3);
+    sql = "select 
last_by(time,num+1),last_by(num+1,time),last_by(num+1,floatnum+1) from table0";
+    retArray = new String[] {"1971-08-20T11:33:20.000Z,16,16,"};
+    tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE_NAME);
   }
 
   @Test
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TableAggregationTableScanOperator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TableAggregationTableScanOperator.java
index a96240f62b7..e5b3e2b1b3f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TableAggregationTableScanOperator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TableAggregationTableScanOperator.java
@@ -40,6 +40,7 @@ import 
org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource;
 import org.apache.tsfile.block.column.Column;
 import org.apache.tsfile.block.column.ColumnBuilder;
 import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.file.metadata.StringArrayDeviceID;
 import org.apache.tsfile.file.metadata.statistics.Statistics;
 import org.apache.tsfile.file.metadata.statistics.StringStatistics;
 import org.apache.tsfile.read.common.TimeRange;
@@ -55,6 +56,7 @@ import org.apache.tsfile.write.schema.IMeasurementSchema;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
@@ -170,7 +172,7 @@ public class TableAggregationTableScanOperator extends 
AbstractSeriesAggregation
     this.maxReturnSize = maxReturnSize;
     this.maxTsBlockLineNum = maxTsBlockLineNum;
 
-    this.seriesScanUtil = 
constructAlignedSeriesScanUtil(deviceEntries.get(currentDeviceIndex));
+    constructAlignedSeriesScanUtil();
   }
 
   @Override
@@ -253,17 +255,27 @@ public class TableAggregationTableScanOperator extends 
AbstractSeriesAggregation
     return resultTsBlock;
   }
 
-  private AlignedSeriesScanUtil constructAlignedSeriesScanUtil(DeviceEntry 
deviceEntry) {
+  private void constructAlignedSeriesScanUtil() {
+    DeviceEntry deviceEntry;
+
+    if (this.deviceEntries.isEmpty() || 
this.deviceEntries.get(this.currentDeviceIndex) == null) {
+      // for device which is not exist
+      deviceEntry = new DeviceEntry(new StringArrayDeviceID(""), 
Collections.emptyList());
+    } else {
+      deviceEntry = this.deviceEntries.get(this.currentDeviceIndex);
+    }
+
     AlignedFullPath alignedPath =
         constructAlignedPath(deviceEntry, measurementColumnNames, 
measurementSchemas);
 
-    return new AlignedSeriesScanUtil(
-        alignedPath,
-        scanOrder,
-        seriesScanOptions,
-        operatorContext.getInstanceContext(),
-        true,
-        measurementColumnTSDataTypes);
+    this.seriesScanUtil =
+        new AlignedSeriesScanUtil(
+            alignedPath,
+            scanOrder,
+            seriesScanOptions,
+            operatorContext.getInstanceContext(),
+            true,
+            measurementColumnTSDataTypes);
   }
 
   /** Return true if we have the result of this timeRange. */
@@ -313,7 +325,7 @@ public class TableAggregationTableScanOperator extends 
AbstractSeriesAggregation
 
       if (currentDeviceIndex < deviceCount) {
         // construct AlignedSeriesScanUtil for next device
-        this.seriesScanUtil = 
constructAlignedSeriesScanUtil(deviceEntries.get(currentDeviceIndex));
+        constructAlignedSeriesScanUtil();
         queryDataSource.reset();
         this.seriesScanUtil.initQueryDataSource(queryDataSource);
       }
@@ -790,8 +802,7 @@ public class TableAggregationTableScanOperator extends 
AbstractSeriesAggregation
 
         if (currentDeviceIndex < deviceCount) {
           // construct AlignedSeriesScanUtil for next device
-          this.seriesScanUtil =
-              
constructAlignedSeriesScanUtil(deviceEntries.get(currentDeviceIndex));
+          constructAlignedSeriesScanUtil();
           queryDataSource.reset();
           this.seriesScanUtil.initQueryDataSource(queryDataSource);
         }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/TableModelStatementMemorySourceVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/TableModelStatementMemorySourceVisitor.java
index ec603a90e83..e87f2d24a2b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/TableModelStatementMemorySourceVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/TableModelStatementMemorySourceVisitor.java
@@ -75,9 +75,9 @@ public class TableModelStatementMemorySourceVisitor
                 symbolAllocator,
                 NOOP)
             .plan(context.getAnalysis());
-    if (context.getAnalysis().isEmptyDataSource()) {
-      return new StatementMemorySource(new TsBlock(0), header);
-    }
+    //    if (context.getAnalysis().isEmptyDataSource()) {
+    //      return new StatementMemorySource(new TsBlock(0), header);
+    //    }
 
     // Generate table model distributed plan
     final TableDistributedPlanGenerator.PlanContext planContext =
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 5dd0c61bd75..3f70b46398c 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
@@ -671,7 +671,7 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
     boolean canUseStatistics =
         !TSDataType.BLOB.equals(node.getSeriesPath().getSeriesType())
             || (aggregationDescriptors.stream()
-                .noneMatch(o -> !judgeCanUseStatistics(o.getAggregationType(), 
TSDataType.BLOB)));
+                .allMatch(o -> judgeCanUseStatistics(o.getAggregationType(), 
TSDataType.BLOB)));
     SeriesAggregationScanOperator aggregateScanOperator =
         new SeriesAggregationScanOperator(
             node.getPlanNodeId(),
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 84b28912569..d33f0b24481 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
@@ -696,7 +696,7 @@ public class PlanGraphPrinter extends 
PlanVisitor<List<String>, PlanGraphPrinter
         String.format(
             "RegionId: %s",
             node.getRegionReplicaSet() == null || 
node.getRegionReplicaSet().getRegionId() == null
-                ? ""
+                ? "Not Assigned"
                 : node.getRegionReplicaSet().getRegionId().getId()));
     return render(node, boxValue, context);
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java
index 32f0e5aec92..76b80d842eb 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.db.queryengine.plan.relational.analyzer;
 
 import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
 import org.apache.iotdb.commons.partition.DataPartition;
 import org.apache.iotdb.commons.partition.SchemaPartition;
@@ -68,8 +69,10 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Streams;
 import com.google.errorprone.annotations.Immutable;
+import org.apache.tsfile.file.metadata.IDeviceID;
 import org.apache.tsfile.read.common.block.TsBlock;
 import org.apache.tsfile.read.common.type.Type;
+import org.apache.tsfile.read.filter.basic.Filter;
 import org.apache.tsfile.utils.TimeDuration;
 
 import javax.annotation.Nullable;
@@ -332,8 +335,9 @@ public class Analysis implements IAnalysis {
     return aggregates.get(NodeRef.of(query));
   }
 
-  public boolean hasAggregates() {
-    return !aggregates.isEmpty();
+  public boolean noAggregates() {
+    return aggregates.isEmpty()
+        || (aggregates.size() == 1 && 
aggregates.entrySet().iterator().next().getValue().isEmpty());
   }
 
   public void setOrderByAggregates(OrderBy node, List<Expression> aggregates) {
@@ -809,6 +813,15 @@ public class Analysis implements IAnalysis {
     redirectNodeList.add(endPoint);
   }
 
+  public List<TRegionReplicaSet> getDataRegionReplicaSetWithTimeFilter(
+      String database, IDeviceID deviceId, Filter timeFilter) {
+    if (dataPartition == null) {
+      return Collections.singletonList(new TRegionReplicaSet());
+    } else {
+      return dataPartition.getDataRegionReplicaSetWithTimeFilter(database, 
deviceId, timeFilter);
+    }
+  }
+
   @Override
   public TimePredicate getCovertedTimePredicate() {
     return null;
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
index 848dbf4c917..925bc23fb2a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
@@ -566,22 +566,26 @@ public class TableDistributedPlanGenerator
   public List<PlanNode> visitAggregationTableScan(
       AggregationTableScanNode node, PlanContext context) {
 
-    Map<TRegionReplicaSet, AggregationTableScanNode> tableScanNodeMap = new 
HashMap<>();
     boolean needSplit = false;
     List<List<TRegionReplicaSet>> regionReplicaSetsList = new ArrayList<>();
     for (DeviceEntry deviceEntry : node.getDeviceEntries()) {
       List<TRegionReplicaSet> regionReplicaSets =
-          analysis
-              .getDataPartitionInfo()
-              .getDataRegionReplicaSetWithTimeFilter(
-                  node.getQualifiedObjectName().getDatabaseName(),
-                  deviceEntry.getDeviceID(),
-                  node.getTimeFilter());
+          analysis.getDataRegionReplicaSetWithTimeFilter(
+              node.getQualifiedObjectName().getDatabaseName(),
+              deviceEntry.getDeviceID(),
+              node.getTimeFilter());
       if (regionReplicaSets.size() > 1) {
         needSplit = true;
       }
       regionReplicaSetsList.add(regionReplicaSets);
     }
+
+    if (regionReplicaSetsList.isEmpty()) {
+      regionReplicaSetsList =
+          Collections.singletonList(Collections.singletonList(new 
TRegionReplicaSet()));
+    }
+
+    Map<TRegionReplicaSet, AggregationTableScanNode> regionNodeMap = new 
HashMap<>();
     // Step is SINGLE, has date_bin(time) and device data in more than one 
region, we need to split
     // this node into two-stage Aggregation
     needSplit = needSplit && node.getProjection() != null && node.getStep() == 
SINGLE;
@@ -591,78 +595,15 @@ public class TableDistributedPlanGenerator
           split(node, symbolAllocator, queryId);
       finalAggregation = splitResult.left;
       AggregationTableScanNode partialAggregation = splitResult.right;
-      for (int i = 0; i < regionReplicaSetsList.size(); i++) {
-        for (TRegionReplicaSet regionReplicaSet : 
regionReplicaSetsList.get(i)) {
-          AggregationTableScanNode aggregationTableScanNode =
-              tableScanNodeMap.computeIfAbsent(
-                  regionReplicaSet,
-                  k -> {
-                    AggregationTableScanNode scanNode =
-                        new AggregationTableScanNode(
-                            queryId.genPlanNodeId(),
-                            partialAggregation.getQualifiedObjectName(),
-                            partialAggregation.getOutputSymbols(),
-                            partialAggregation.getAssignments(),
-                            new ArrayList<>(),
-                            partialAggregation.getIdAndAttributeIndexMap(),
-                            partialAggregation.getScanOrder(),
-                            partialAggregation.getTimePredicate().orElse(null),
-                            partialAggregation.getPushDownPredicate(),
-                            partialAggregation.getPushDownLimit(),
-                            partialAggregation.getPushDownOffset(),
-                            partialAggregation.isPushLimitToEachDevice(),
-                            partialAggregation.getProjection(),
-                            partialAggregation.getAggregations(),
-                            partialAggregation.getGroupingSets(),
-                            partialAggregation.getPreGroupedSymbols(),
-                            partialAggregation.getStep(),
-                            partialAggregation.getGroupIdSymbol());
-                    scanNode.setRegionReplicaSet(regionReplicaSet);
-                    return scanNode;
-                  });
-          
aggregationTableScanNode.appendDeviceEntry(node.getDeviceEntries().get(i));
-        }
-      }
+      buildRegionNodeMap(node, regionReplicaSetsList, regionNodeMap, 
partialAggregation);
     } else {
-      for (int i = 0; i < regionReplicaSetsList.size(); i++) {
-        for (TRegionReplicaSet regionReplicaSet : 
regionReplicaSetsList.get(i)) {
-          AggregationTableScanNode aggregationTableScanNode =
-              tableScanNodeMap.computeIfAbsent(
-                  regionReplicaSet,
-                  k -> {
-                    AggregationTableScanNode scanNode =
-                        new AggregationTableScanNode(
-                            queryId.genPlanNodeId(),
-                            node.getQualifiedObjectName(),
-                            node.getOutputSymbols(),
-                            node.getAssignments(),
-                            new ArrayList<>(),
-                            node.getIdAndAttributeIndexMap(),
-                            node.getScanOrder(),
-                            node.getTimePredicate().orElse(null),
-                            node.getPushDownPredicate(),
-                            node.getPushDownLimit(),
-                            node.getPushDownOffset(),
-                            node.isPushLimitToEachDevice(),
-                            node.getProjection(),
-                            node.getAggregations(),
-                            node.getGroupingSets(),
-                            node.getPreGroupedSymbols(),
-                            node.getStep(),
-                            node.getGroupIdSymbol());
-                    scanNode.setRegionReplicaSet(regionReplicaSet);
-                    return scanNode;
-                  });
-          
aggregationTableScanNode.appendDeviceEntry(node.getDeviceEntries().get(i));
-        }
-      }
+      buildRegionNodeMap(node, regionReplicaSetsList, regionNodeMap, node);
     }
 
     List<PlanNode> resultTableScanNodeList = new ArrayList<>();
     TRegionReplicaSet mostUsedDataRegion = null;
     int maxDeviceEntrySizeOfTableScan = 0;
-    for (Map.Entry<TRegionReplicaSet, AggregationTableScanNode> entry :
-        tableScanNodeMap.entrySet()) {
+    for (Map.Entry<TRegionReplicaSet, AggregationTableScanNode> entry : 
regionNodeMap.entrySet()) {
       TRegionReplicaSet regionReplicaSet = entry.getKey();
       TableScanNode subTableScanNode = entry.getValue();
       subTableScanNode.setPlanNodeId(queryId.genPlanNodeId());
@@ -698,6 +639,49 @@ public class TableDistributedPlanGenerator
     return resultTableScanNodeList;
   }
 
+  private void buildRegionNodeMap(
+      AggregationTableScanNode originalAggTableScanNode,
+      List<List<TRegionReplicaSet>> regionReplicaSetsList,
+      Map<TRegionReplicaSet, AggregationTableScanNode> regionNodeMap,
+      AggregationTableScanNode partialAggTableScanNode) {
+    for (int i = 0; i < regionReplicaSetsList.size(); i++) {
+      for (TRegionReplicaSet regionReplicaSet : regionReplicaSetsList.get(i)) {
+        AggregationTableScanNode aggregationTableScanNode =
+            regionNodeMap.computeIfAbsent(
+                regionReplicaSet,
+                k -> {
+                  AggregationTableScanNode scanNode =
+                      new AggregationTableScanNode(
+                          queryId.genPlanNodeId(),
+                          partialAggTableScanNode.getQualifiedObjectName(),
+                          partialAggTableScanNode.getOutputSymbols(),
+                          partialAggTableScanNode.getAssignments(),
+                          new ArrayList<>(),
+                          partialAggTableScanNode.getIdAndAttributeIndexMap(),
+                          partialAggTableScanNode.getScanOrder(),
+                          
partialAggTableScanNode.getTimePredicate().orElse(null),
+                          partialAggTableScanNode.getPushDownPredicate(),
+                          partialAggTableScanNode.getPushDownLimit(),
+                          partialAggTableScanNode.getPushDownOffset(),
+                          partialAggTableScanNode.isPushLimitToEachDevice(),
+                          partialAggTableScanNode.getProjection(),
+                          partialAggTableScanNode.getAggregations(),
+                          partialAggTableScanNode.getGroupingSets(),
+                          partialAggTableScanNode.getPreGroupedSymbols(),
+                          partialAggTableScanNode.getStep(),
+                          partialAggTableScanNode.getGroupIdSymbol());
+                  scanNode.setRegionReplicaSet(regionReplicaSet);
+                  return scanNode;
+                });
+        if (originalAggTableScanNode.getDeviceEntries().size() > i
+            && originalAggTableScanNode.getDeviceEntries().get(i) != null) {
+          aggregationTableScanNode.appendDeviceEntry(
+              originalAggTableScanNode.getDeviceEntries().get(i));
+        }
+      }
+    }
+  }
+
   private static OrderingScheme constructOrderingSchema(List<Symbol> symbols) {
     Map<Symbol, SortOrder> orderings = new HashMap<>();
     symbols.forEach(symbol -> orderings.put(symbol, SortOrder.ASC_NULLS_LAST));
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushAggregationIntoTableScan.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushAggregationIntoTableScan.java
index d5d4defd8c6..e5dcffd9ea0 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushAggregationIntoTableScan.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushAggregationIntoTableScan.java
@@ -56,7 +56,7 @@ public class PushAggregationIntoTableScan implements 
PlanOptimizer {
   @Override
   public PlanNode optimize(PlanNode plan, PlanOptimizer.Context context) {
     if (!(context.getAnalysis().getStatement() instanceof Query)
-        || !context.getAnalysis().hasAggregates()) {
+        || context.getAnalysis().noAggregates()) {
       return plan;
     }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
index 9db2f87cfd9..8e9296b7f48 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
@@ -483,7 +483,10 @@ public class PushPredicateIntoTableScan implements 
PlanOptimizer {
           .recordPlanCost(TABLE_TYPE, SCHEMA_FETCHER, System.nanoTime() - 
startTime);
 
       if (deviceEntries.isEmpty()) {
-        analysis.setFinishQueryAfterAnalyze();
+        if (analysis.noAggregates()) {
+          // no device entries, queries(except aggregation) can be finished
+          analysis.setFinishQueryAfterAnalyze();
+        }
         analysis.setEmptyDataSource(true);
       } else {
         Filter timeFilter =
@@ -506,7 +509,10 @@ public class PushPredicateIntoTableScan implements 
PlanOptimizer {
         }
 
         if (dataPartition.getDataPartitionMap().isEmpty()) {
-          analysis.setFinishQueryAfterAnalyze();
+          if (analysis.noAggregates()) {
+            // no data partitions, queries(except aggregation) can be finished
+            analysis.setFinishQueryAfterAnalyze();
+          }
           analysis.setEmptyDataSource(true);
         } else {
           analysis.upsertDataPartition(dataPartition);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformAggregationToStreamable.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformAggregationToStreamable.java
index 006fbb4c87d..1ca5e2bbcd2 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformAggregationToStreamable.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformAggregationToStreamable.java
@@ -50,7 +50,7 @@ public class TransformAggregationToStreamable implements 
PlanOptimizer {
   @Override
   public PlanNode optimize(PlanNode plan, PlanOptimizer.Context context) {
     if (!(context.getAnalysis().getStatement() instanceof Query)
-        || !context.getAnalysis().hasAggregates()) {
+        || context.getAnalysis().noAggregates()) {
       return plan;
     }
 

Reply via email to