@Override
  public Operator visitWindowFunction(WindowNode node, LocalExecutionPlanContext context) {
    TypeProvider typeProvider = context.getTypeProvider();
    Operator child = node.getChild().accept(this, context);
    OperatorContext operatorContext =
        context
            .getDriverContext()
            .addOperatorContext(
                context.getNextOperatorId(),
                node.getPlanNodeId(),
                TableWindowOperator.class.getSimpleName());

    Map<Symbol, Integer> childLayout =
        makeLayoutFromOutputSymbols(node.getChild().getOutputSymbols());

    // Partition channel
    List<Symbol> partitionBySymbols = node.getSpecification().getPartitionBy();
    List<Integer> partitionChannels =
        ImmutableList.copyOf(getChannelsForSymbols(partitionBySymbols, childLayout));

    // Sort channel
    List<Integer> sortChannels = ImmutableList.of();
    List<SortOrder> sortOrder = ImmutableList.of();
    if (node.getSpecification().getOrderingScheme().isPresent()) {
      OrderingScheme orderingScheme = node.getSpecification().getOrderingScheme().get();
      sortChannels = getChannelsForSymbols(orderingScheme.getOrderBy(), childLayout);
      sortOrder = orderingScheme.getOrderingList();
    }

    // Output channel
    ImmutableList.Builder<Integer> outputChannels = ImmutableList.builder();
    List<TSDataType> outputDataTypes = new ArrayList<>();
    List<TSDataType> inputDataTypes =
        getOutputColumnTypes(node.getChild(), context.getTypeProvider());
    for (int i = 0; i < inputDataTypes.size(); i++) {
      outputChannels.add(i);
      outputDataTypes.add(inputDataTypes.get(i));
    }

    // Window functions
    List<FrameInfo> frameInfoList = new ArrayList<>();
    List<WindowFunction> windowFunctions = new ArrayList<>();
    List<Symbol> windowFunctionOutputSymbols = new ArrayList<>();
    List<TSDataType> windowFunctionOutputDataTypes = new ArrayList<>();
    for (Map.Entry<Symbol, WindowNode.Function> entry : node.getWindowFunctions().entrySet()) {
      // Create FrameInfo
      WindowNode.Frame frame = entry.getValue().getFrame();

      Optional<Integer> frameStartChannel = Optional.empty();
      if (frame.getStartValue().isPresent()) {
        frameStartChannel = Optional.ofNullable(childLayout.get(frame.getStartValue().get()));
      }
      Optional<Integer> frameEndChannel = Optional.empty();
      if (frame.getEndValue().isPresent()) {
        frameEndChannel = Optional.ofNullable(childLayout.get(frame.getEndValue().get()));
      }

      Optional<Integer> sortKeyChannel = Optional.empty();
      Optional<SortOrder> ordering = Optional.empty();
      if (node.getSpecification().getOrderingScheme().isPresent()) {
        sortKeyChannel = Optional.of(sortChannels.get(0));
        if (sortOrder.get(0).isNullsFirst()) {
          if (sortOrder.get(0).isAscending()) {
            ordering = Optional.of(ASC_NULLS_FIRST);
          } else {
            ordering = Optional.of(DESC_NULLS_FIRST);
          }
        } else {
          if (sortOrder.get(0).isAscending()) {
            ordering = Optional.of(ASC_NULLS_LAST);
          } else {
            ordering = Optional.of(DESC_NULLS_LAST);
          }
        }
      }
      FrameInfo frameInfo =
          new FrameInfo(
              frame.getType(),
              frame.getStartType(),
              frameStartChannel,
              frame.getEndType(),
              frameEndChannel,
              sortKeyChannel,
              ordering);
      frameInfoList.add(frameInfo);

      // Arguments
      WindowNode.Function function = entry.getValue();
      ResolvedFunction resolvedFunction = function.getResolvedFunction();
      List<Integer> argumentChannels = new ArrayList<>();
      for (Expression argument : function.getArguments()) {
        Symbol argumentSymbol = Symbol.from(argument);
        argumentChannels.add(childLayout.get(argumentSymbol));
      }

      // Return value
      Type returnType = resolvedFunction.getSignature().getReturnType();
      windowFunctionOutputDataTypes.add(getTSDataType(returnType));

      // Window function
      Symbol symbol = entry.getKey();
      WindowFunction windowFunction;
      FunctionKind functionKind = resolvedFunction.getFunctionKind();
      if (functionKind == FunctionKind.AGGREGATE) {
        WindowAggregator tableWindowAggregator =
            buildWindowAggregator(symbol, function, typeProvider, argumentChannels);
        windowFunction = new AggregationWindowFunction(tableWindowAggregator);
      } else if (functionKind == FunctionKind.WINDOW) {
        String functionName = function.getResolvedFunction().getSignature().getName();
        windowFunction =
            WindowFunctionFactory.createBuiltinWindowFunction(
                functionName, argumentChannels, function.isIgnoreNulls());
      } else {
        throw new UnsupportedOperationException("Unsupported function kind: " + functionKind);
      }

      windowFunctions.add(windowFunction);
      windowFunctionOutputSymbols.add(symbol);
    }

    // Compute layout
    ImmutableMap.Builder<Symbol, Integer> outputMappings = ImmutableMap.builder();
    for (Symbol symbol : node.getChild().getOutputSymbols()) {
      outputMappings.put(symbol, childLayout.get(symbol));
    }
    int channel = inputDataTypes.size();

    for (Symbol symbol : windowFunctionOutputSymbols) {
      outputMappings.put(symbol, channel);
      channel++;
    }

    outputDataTypes.addAll(windowFunctionOutputDataTypes);
    return new TableWindowOperator(
        operatorContext,
        child,
        inputDataTypes,
        outputDataTypes,
        outputChannels.build(),
        windowFunctions,
        frameInfoList,
        partitionChannels,
        sortChannels);
  }
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