@Override
  public PlanNode visitQuery(QueryStatement queryStatement, MPPQueryContext context) {
    if (analysis.allDevicesInOneTemplate()) {
      return new TemplatedLogicalPlan(analysis, queryStatement, context).visitQuery();
    }

    LogicalPlanBuilder planBuilder = new LogicalPlanBuilder(analysis, context);

    if (queryStatement.isLastQuery()) {
      planBuilder = planBuilder.planLast(analysis, analysis.getTimeseriesOrderingForLastQuery());

      if (queryStatement.hasOrderBy() && !queryStatement.onlyOrderByTimeseries()) {
        planBuilder = planBuilder.planOrderBy(queryStatement.getSortItemList());
      }

      planBuilder =
          planBuilder
              .planOffset(queryStatement.getRowOffset())
              .planLimit(queryStatement.getRowLimit());
      return planBuilder.getRoot();
    }

    if (queryStatement.isAlignByDevice()) {
      Map<IDeviceID, PlanNode> deviceToSubPlanMap = new LinkedHashMap<>();
      for (PartialPath device : analysis.getDeviceList()) {
        IDeviceID deviceID = device.getIDeviceIDAsFullDevice();
        LogicalPlanBuilder subPlanBuilder = new LogicalPlanBuilder(analysis, context);
        subPlanBuilder =
            subPlanBuilder.withNewRoot(
                visitQueryBody(
                    queryStatement,
                    analysis.getDeviceToSourceExpressions().get(deviceID),
                    analysis.getDeviceToSourceTransformExpressions().get(deviceID),
                    analysis.getDeviceToWhereExpression() != null
                        ? analysis.getDeviceToWhereExpression().get(deviceID)
                        : null,
                    analysis.getDeviceToAggregationExpressions().get(deviceID),
                    analysis.getDeviceToGroupByExpression() != null
                        ? analysis.getDeviceToGroupByExpression().get(deviceID)
                        : null,
                    context));
        // order by device, expression, push down sortOperator
        if (queryStatement.needPushDownSort()) {
          subPlanBuilder =
              subPlanBuilder.planOrderBy(
                  analysis.getDeviceToOrderByExpressions().get(deviceID),
                  analysis.getDeviceToSortItems().get(deviceID));
        }
        deviceToSubPlanMap.put(deviceID, subPlanBuilder.getRoot());
      }

      // convert to ALIGN BY DEVICE view
      planBuilder =
          planBuilder.planDeviceView(
              deviceToSubPlanMap,
              analysis.getDeviceViewOutputExpressions(),
              analysis.getDeviceViewInputIndexesMap(),
              analysis.getSelectExpressions(),
              queryStatement,
              analysis);
    } else {
      planBuilder =
          planBuilder.withNewRoot(
              visitQueryBody(
                  queryStatement,
                  analysis.getSourceExpressions(),
                  analysis.getSourceTransformExpressions(),
                  analysis.getWhereExpression(),
                  analysis.getAggregationExpressions(),
                  analysis.getGroupByExpression(),
                  context));
    }

    if (queryStatement.isAggregationQuery()) {
      planBuilder =
          planBuilder.planHavingAndTransform(
              analysis.getHavingExpression(),
              analysis.getSelectExpressions(),
              analysis.getOrderByExpressions(),
              queryStatement.isGroupByTime(),
              queryStatement.getResultTimeOrder());
    }

    if (!queryStatement.needPushDownSort()) {
      planBuilder = planBuilder.planOrderBy(queryStatement, analysis);
    }

    // other upstream node
    planBuilder =
        planBuilder
            .planFill(analysis.getFillDescriptor(), queryStatement.getResultTimeOrder())
            .planOffset(queryStatement.getRowOffset());

    if (!analysis.isUseTopKNode() || queryStatement.hasOffset()) {
      planBuilder = planBuilder.planLimit(queryStatement.getRowLimit());
    }

    if (queryStatement.hasModelInference()) {
      planBuilder = planBuilder.planInference(analysis);
    }

    // plan select into
    if (queryStatement.isAlignByDevice()) {
      planBuilder = planBuilder.planDeviceViewInto(analysis.getDeviceViewIntoPathDescriptor());
    } else {
      planBuilder = planBuilder.planInto(analysis.getIntoPathDescriptor());
    }

    return planBuilder.getRoot();
  }
This visitor is used to generate a logical plan for the statement and returns the {@link
PlanNode}.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java