private DeviceIteratorScanOperator.TreeNonAlignedDeviceViewScanParameters
      constructTreeNonAlignedDeviceViewScanOperatorParameter(
          TreeNonAlignedDeviceViewScanNode node,
          LocalExecutionPlanContext context,
          String className,
          Map<String, String> fieldColumnsRenameMap,
          IDeviceID.TreeDeviceIdColumnValueExtractor extractor,
          long viewTTL) {
    if (node.isPushLimitToEachDevice() && node.getPushDownOffset() > 0) {
      throw new IllegalArgumentException(
          "PushDownOffset should not be set when isPushLimitToEachDevice is true.");
    }
    CommonTableScanOperatorParameters commonParameter =
        new CommonTableScanOperatorParameters(node, fieldColumnsRenameMap, true);
    List<IMeasurementSchema> measurementSchemas = commonParameter.measurementSchemas;
    List<String> measurementColumnNames = commonParameter.measurementColumnNames;
    List<ColumnSchema> fullColumnSchemas = commonParameter.columnSchemas;
    int[] columnsIndexArray = commonParameter.columnsIndexArray;

    boolean isSingleColumn = measurementSchemas.size() == 1;

    OperatorContext operatorContext =
        context
            .getDriverContext()
            .addOperatorContext(context.getNextOperatorId(), node.getPlanNodeId(), className);

    Set<String> allSensors = new HashSet<>(measurementColumnNames);

    DeviceIteratorScanOperator.DeviceChildOperatorTreeGenerator deviceChildOperatorTreeGenerator =
        new DeviceIteratorScanOperator.DeviceChildOperatorTreeGenerator() {

          private Operator operator;
          private List<SeriesScanOptions> seriesScanOptionsList;
          private List<Operator> seriesScanOperators;
          private FilterAndProjectOperator filterAndProjectOperator;
          private OffsetOperator reuseOffsetOperator;
          private LimitOperator reuseLimitOperator;
          private Operator startCloseInternalOperator;

          private List<Expression> cannotPushDownConjuncts;
          private boolean removeUpperOffsetAndLimitOperator;

          @Override
          public boolean keepOffsetAndLimitOperatorAfterDeviceIterator() {
            calculateSeriesScanOptionsList();
            return !removeUpperOffsetAndLimitOperator && !node.isPushLimitToEachDevice();
          }

          @Override
          public void generateCurrentDeviceOperatorTree(DeviceEntry deviceEntry) {
            calculateSeriesScanOptionsList();
            operator = constructTreeToTableViewAdaptorOperator(deviceEntry);
            if (isSingleColumn) {
              return;
            }
            if (!cannotPushDownConjuncts.isEmpty()
                || node.getAssignments().size() != node.getOutputSymbols().size()) {
              operator = getFilterAndProjectOperator(operator);
            }
            if (!node.isPushLimitToEachDevice() || removeUpperOffsetAndLimitOperator) {
              return;
            }
            if (node.getPushDownLimit() > 0) {
              operator = new LimitOperator(operatorContext, node.getPushDownLimit(), operator);
            }
          }

          private void calculateSeriesScanOptionsList() {
            if (seriesScanOptionsList != null) {
              return;
            }
            seriesScanOptionsList = new ArrayList<>(measurementSchemas.size());
            cannotPushDownConjuncts = new ArrayList<>();
            Map<String, List<Expression>> pushDownConjunctsForEachMeasurement = new HashMap<>();
            if (node.getPushDownPredicate() != null) {
              List<Expression> conjuncts = IrUtils.extractConjuncts(node.getPushDownPredicate());
              for (Expression conjunct : conjuncts) {
                Set<Symbol> symbols = SymbolsExtractor.extractUnique(conjunct);
                boolean containsMultiDataSource = symbols.size() > 1;
                if (containsMultiDataSource) {
                  cannotPushDownConjuncts.add(conjunct);
                  continue;
                }
                String symbolName = symbols.iterator().next().getName();
                pushDownConjunctsForEachMeasurement
                    .computeIfAbsent(symbolName, k -> new ArrayList<>())
                    .add(conjunct);
              }
            }

            boolean canPushDownLimit = cannotPushDownConjuncts.isEmpty();
            // only use full outer time join
            boolean canPushDownLimitToAllSeriesScanOptions =
                canPushDownLimit && pushDownConjunctsForEachMeasurement.isEmpty();
            // the left child of LeftOuterTimeJoinOperator is SeriesScanOperator
            boolean pushDownOffsetAndLimitToLeftChildSeriesScanOperator =
                canPushDownLimit && pushDownConjunctsForEachMeasurement.size() == 1;
            // the left child of LeftOuterTimeJoinOperator is InnerTimeJoinOperator
            boolean pushDownOffsetAndLimitAfterInnerJoinOperator =
                canPushDownLimit && pushDownConjunctsForEachMeasurement.size() > 1;
            removeUpperOffsetAndLimitOperator =
                pushDownOffsetAndLimitToLeftChildSeriesScanOperator
                    || pushDownOffsetAndLimitAfterInnerJoinOperator
                    || isSingleColumn;
            for (int i = 0; i < measurementSchemas.size(); i++) {
              IMeasurementSchema measurementSchema = measurementSchemas.get(i);
              List<Expression> pushDownPredicatesForCurrentMeasurement =
                  pushDownConjunctsForEachMeasurement.get(measurementSchema.getMeasurementName());
              Expression pushDownPredicateForCurrentMeasurement =
                  isSingleColumn
                      ? node.getPushDownPredicate()
                      : (pushDownPredicatesForCurrentMeasurement == null
                          ? null
                          : IrUtils.combineConjuncts(pushDownPredicatesForCurrentMeasurement));
              SeriesScanOptions.Builder builder =
                  node.getTimePredicate()
                      .map(expression -> getSeriesScanOptionsBuilder(context, expression))
                      .orElseGet(SeriesScanOptions.Builder::new);
              builder
                  .withIsTableViewForTreeModel(true)
                  .withAllSensors(new HashSet<>(measurementColumnNames));
              if (pushDownPredicateForCurrentMeasurement != null) {
                builder.withPushDownFilter(
                    convertPredicateToFilter(
                        pushDownPredicateForCurrentMeasurement,
                        Collections.singletonMap(measurementSchema.getMeasurementName(), 0),
                        commonParameter.columnSchemaMap,
                        commonParameter.timeColumnName));
              }
              if (isSingleColumn
                  || (pushDownOffsetAndLimitToLeftChildSeriesScanOperator
                      && pushDownPredicateForCurrentMeasurement != null)) {
                builder.withPushDownLimit(node.getPushDownLimit());
                builder.withPushLimitToEachDevice(node.isPushLimitToEachDevice());
              }

              // In the case of single column, both offset and limit are pushed down to the
              // SeriesScanOperator
              if (!isSingleColumn && canPushDownLimitToAllSeriesScanOptions) {
                builder.withPushDownLimit(node.getPushDownLimit() + node.getPushDownOffset());
              }
              if (isSingleColumn
                  || (pushDownOffsetAndLimitToLeftChildSeriesScanOperator
                      && pushDownPredicateForCurrentMeasurement != null)) {
                builder.withPushDownOffset(
                    node.isPushLimitToEachDevice() ? 0 : node.getPushDownOffset());
              }
              SeriesScanOptions options = builder.build();
              options.setTTLForTableView(viewTTL);
              seriesScanOptionsList.add(options);
            }
          }

          private Operator constructTreeToTableViewAdaptorOperator(DeviceEntry deviceEntry) {
            seriesScanOperators = new ArrayList<>(measurementSchemas.size());
            operator = constructAndJoinScanOperators(deviceEntry);
            return new TreeToTableViewAdaptorOperator(
                operatorContext,
                deviceEntry,
                columnsIndexArray,
                fullColumnSchemas,
                operator,
                extractor);
          }

          private Operator constructAndJoinScanOperators(DeviceEntry deviceEntry) {
            List<Operator> childrenWithPushDownPredicate = new ArrayList<>();
            List<TSDataType> innerJoinDataTypeList = new ArrayList<>();
            List<Operator> childrenWithoutPushDownPredicate = new ArrayList<>();
            List<TSDataType> fullOuterTimeJoinDataTypeList = new ArrayList<>();
            Map<InputLocation, Integer> leftOuterJoinColumnIndexMap = new HashMap<>();
            for (int i = 0; i < measurementSchemas.size(); i++) {
              IMeasurementSchema measurementSchema = measurementSchemas.get(i);
              NonAlignedFullPath path =
                  new NonAlignedFullPath(deviceEntry.getDeviceID(), measurementSchema);
              SeriesScanOptions seriesScanOptions = seriesScanOptionsList.get(i);
              Operator seriesScanOperator =
                  new SeriesScanOperator(
                      operatorContext,
                      node.getPlanNodeId(),
                      path,
                      node.getScanOrder(),
                      seriesScanOptions);
              seriesScanOperators.add(seriesScanOperator);
              if (seriesScanOptions.getPushDownFilter() != null) {
                childrenWithPushDownPredicate.add(seriesScanOperator);
                innerJoinDataTypeList.add(measurementSchema.getType());
                leftOuterJoinColumnIndexMap.put(
                    new InputLocation(0, childrenWithPushDownPredicate.size() - 1), i);
              } else {
                childrenWithoutPushDownPredicate.add(seriesScanOperator);
                fullOuterTimeJoinDataTypeList.add(measurementSchema.getType());
                leftOuterJoinColumnIndexMap.put(
                    new InputLocation(1, childrenWithoutPushDownPredicate.size() - 1), i);
              }
            }
            Operator leftChild =
                generateInnerTimeJoinOperator(childrenWithPushDownPredicate, innerJoinDataTypeList);
            Operator rightChild =
                generateFullOuterTimeJoinOperator(
                    childrenWithoutPushDownPredicate, fullOuterTimeJoinDataTypeList);
            return generateLeftOuterTimeJoinOperator(
                leftChild,
                rightChild,
                childrenWithPushDownPredicate.size(),
                leftOuterJoinColumnIndexMap,
                IMeasurementSchema.getDataTypeList(measurementSchemas));
          }

          private Operator generateInnerTimeJoinOperator(
              List<Operator> operators, List<TSDataType> dataTypes) {
            if (operators.isEmpty()) {
              return null;
            }
            if (operators.size() == 1) {
              return operators.get(0);
            }
            Map<InputLocation, Integer> outputColumnMap = new HashMap<>();
            for (int i = 0; i < operators.size(); i++) {
              outputColumnMap.put(new InputLocation(i, 0), i);
            }
            Operator currentOperator =
                new InnerTimeJoinOperator(
                    operatorContext,
                    operators,
                    dataTypes,
                    node.getScanOrder() == Ordering.ASC
                        ? new AscTimeComparator()
                        : new DescTimeComparator(),
                    outputColumnMap);
            boolean addOffsetAndLimitOperatorAfterLeftChild =
                operators.size() > 1 && cannotPushDownConjuncts.isEmpty();
            if (addOffsetAndLimitOperatorAfterLeftChild) {
              if (node.getPushDownOffset() > 0) {
                currentOperator = getReuseOffsetOperator(currentOperator);
              }
              if (node.getPushDownLimit() > 0) {
                currentOperator = getReuseLimitOperator(currentOperator);
              }
            }
            return currentOperator;
          }

          private Operator generateFullOuterTimeJoinOperator(
              List<Operator> operators, List<TSDataType> dataTypes) {
            if (operators.isEmpty()) {
              return null;
            }
            if (operators.size() == 1) {
              return operators.get(0);
            }
            List<ColumnMerger> columnMergers = new ArrayList<>(operators.size());
            for (int i = 0; i < operators.size(); i++) {
              columnMergers.add(
                  new SingleColumnMerger(
                      new InputLocation(i, 0),
                      node.getScanOrder() == Ordering.ASC
                          ? new AscTimeComparator()
                          : new DescTimeComparator()));
            }
            return new FullOuterTimeJoinOperator(
                operatorContext,
                operators,
                node.getScanOrder(),
                dataTypes,
                columnMergers,
                node.getScanOrder() == Ordering.ASC
                    ? new AscTimeComparator()
                    : new DescTimeComparator());
          }

          private Operator generateLeftOuterTimeJoinOperator(
              Operator left,
              Operator right,
              int leftColumnCount,
              Map<InputLocation, Integer> outputColumnMap,
              List<TSDataType> dataTypes) {
            if (left == null) {
              return right;
            } else if (right == null) {
              return left;
            } else {
              return new TableLeftOuterTimeJoinOperator(
                  operatorContext,
                  left,
                  right,
                  leftColumnCount,
                  outputColumnMap,
                  dataTypes,
                  node.getScanOrder() == Ordering.ASC
                      ? new AscTimeComparator()
                      : new DescTimeComparator());
            }
          }

          private Operator getReuseOffsetOperator(Operator child) {
            this.reuseOffsetOperator =
                reuseOffsetOperator == null
                    ? new OffsetOperator(operatorContext, node.getPushDownOffset(), child)
                    : new OffsetOperator(reuseOffsetOperator, child);
            return this.reuseOffsetOperator;
          }

          private Operator getReuseLimitOperator(Operator child) {
            this.reuseLimitOperator =
                reuseLimitOperator == null
                    ? new LimitOperator(operatorContext, node.getPushDownLimit(), child)
                    : new LimitOperator(reuseLimitOperator, child);
            return this.reuseLimitOperator;
          }

          private Operator getFilterAndProjectOperator(Operator childOperator) {
            startCloseInternalOperator = childOperator;
            if (filterAndProjectOperator != null) {
              return new FilterAndProjectOperator(filterAndProjectOperator, childOperator);
            }
            List<TSDataType> inputDataTypeList = new ArrayList<>(fullColumnSchemas.size());
            Map<Symbol, List<InputLocation>> symbolInputLocationMap =
                new HashMap<>(fullColumnSchemas.size());
            for (int i = 0; i < fullColumnSchemas.size(); i++) {
              ColumnSchema columnSchema = fullColumnSchemas.get(i);
              symbolInputLocationMap
                  .computeIfAbsent(new Symbol(columnSchema.getName()), key -> new ArrayList<>())
                  .add(new InputLocation(0, i));
              inputDataTypeList.add(getTSDataType(columnSchema.getType()));
            }
            Expression combinedCannotPushDownPredicates =
                cannotPushDownConjuncts.isEmpty()
                    ? null
                    : IrUtils.combineConjuncts(cannotPushDownConjuncts);
            filterAndProjectOperator =
                (FilterAndProjectOperator)
                    TableOperatorGenerator.this.constructFilterAndProjectOperator(
                        Optional.ofNullable(combinedCannotPushDownPredicates),
                        childOperator,
                        node.getOutputSymbols().stream()
                            .map(Symbol::toSymbolReference)
                            .toArray(Expression[]::new),
                        inputDataTypeList,
                        symbolInputLocationMap,
                        node.getPlanNodeId(),
                        context);
            return filterAndProjectOperator;
          }

          @Override
          public Operator getCurrentDeviceRootOperator() {
            return operator;
          }

          @Override
          public List<Operator> getCurrentDeviceDataSourceOperators() {
            return seriesScanOperators;
          }

          @Override
          public Operator getCurrentDeviceStartCloseOperator() {
            return startCloseInternalOperator == null ? operator : startCloseInternalOperator;
          }
        };

    return new DeviceIteratorScanOperator.TreeNonAlignedDeviceViewScanParameters(
        allSensors,
        operatorContext,
        node.getDeviceEntries(),
        measurementColumnNames,
        measurementSchemas,
        deviceChildOperatorTreeGenerator);
  }
This Visitor is responsible for transferring Table PlanNode Tree to Table Operator Tree.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java