private List<PlanNode> constructDeviceTableScanByRegionReplicaSet(
      final DeviceTableScanNode node, final PlanContext context) {
    DataPartition dataPartition = analysis.getDataPartitionInfo();
    if (dataPartition == null) {
      node.setRegionReplicaSet(NOT_ASSIGNED);
      return Collections.singletonList(node);
    }

    String dbName = node.getQualifiedObjectName().getDatabaseName();
    Map<TSeriesPartitionSlot, Map<TTimePartitionSlot, List<TRegionReplicaSet>>> seriesSlotMap =
        dataPartition.getDataPartitionMap().get(dbName);
    if (seriesSlotMap == null) {
      throw new SemanticException(
          String.format("Given queried database: %s is not exist!", dbName));
    }

    final Map<TRegionReplicaSet, DeviceTableScanNode> tableScanNodeMap = new HashMap<>();
    Map<Integer, List<TRegionReplicaSet>> cachedSeriesSlotWithRegions = new HashMap<>();

    for (final DeviceEntry deviceEntry : node.getDeviceEntries()) {
      List<TRegionReplicaSet> regionReplicaSets =
          getDeviceReplicaSets(
              dataPartition,
              seriesSlotMap,
              deviceEntry.getDeviceID(),
              node.getTimeFilter(),
              cachedSeriesSlotWithRegions);
      for (final TRegionReplicaSet regionReplicaSet : regionReplicaSets) {
        final DeviceTableScanNode deviceTableScanNode =
            tableScanNodeMap.computeIfAbsent(
                regionReplicaSet,
                k -> {
                  final DeviceTableScanNode scanNode =
                      new DeviceTableScanNode(
                          queryId.genPlanNodeId(),
                          node.getQualifiedObjectName(),
                          node.getOutputSymbols(),
                          node.getAssignments(),
                          new ArrayList<>(),
                          node.getTagAndAttributeIndexMap(),
                          node.getScanOrder(),
                          node.getTimePredicate().orElse(null),
                          node.getPushDownPredicate(),
                          node.getPushDownLimit(),
                          node.getPushDownOffset(),
                          node.isPushLimitToEachDevice(),
                          node.containsNonAlignedDevice());
                  scanNode.setRegionReplicaSet(regionReplicaSet);
                  return scanNode;
                });
        deviceTableScanNode.appendDeviceEntry(deviceEntry);
      }
    }
    if (tableScanNodeMap.isEmpty()) {
      node.setRegionReplicaSet(NOT_ASSIGNED);
      return Collections.singletonList(node);
    }

    final List<PlanNode> resultTableScanNodeList = new ArrayList<>();
    TRegionReplicaSet mostUsedDataRegion = null;
    int maxDeviceEntrySizeOfTableScan = 0;
    for (final Map.Entry<TRegionReplicaSet, DeviceTableScanNode> entry :
        topology.filterReachableCandidates(tableScanNodeMap.entrySet())) {
      final DeviceTableScanNode subDeviceTableScanNode = entry.getValue();
      resultTableScanNodeList.add(subDeviceTableScanNode);

      if (mostUsedDataRegion == null
          || subDeviceTableScanNode.getDeviceEntries().size() > maxDeviceEntrySizeOfTableScan) {
        mostUsedDataRegion = entry.getKey();
        maxDeviceEntrySizeOfTableScan = subDeviceTableScanNode.getDeviceEntries().size();
      }
    }
    if (mostUsedDataRegion == null) {
      throw new RootFIPlacementException(tableScanNodeMap.keySet());
    }
    context.mostUsedRegion = mostUsedDataRegion;

    if (!context.hasSortProperty) {
      return resultTableScanNodeList;
    }

    processSortProperty(node, resultTableScanNodeList, context);
    return resultTableScanNodeList;
  }
This class is used to generate distributed plan for table model.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java