@Override
    protected Scope visitQuerySpecification(QuerySpecification node, Optional<Scope> scope) {
      // TODO: extract candidate names from SELECT, WHERE, HAVING, GROUP BY and ORDER BY expressions
      // to pass down to analyzeFrom
      hasFillInParentScope = node.getFill().isPresent() || hasFillInParentScope;

      Scope sourceScope = analyzeFrom(node, scope);
      analyzeWindowDefinitions(node, sourceScope);
      resolveFunctionCallAndMeasureWindows(node);

      node.getWhere().ifPresent(where -> analyzeWhere(node, sourceScope, where));

      List<Expression> outputExpressions = analyzeSelect(node, sourceScope);
      Analysis.GroupingSetAnalysis groupByAnalysis =
          analyzeGroupBy(node, sourceScope, outputExpressions);
      analyzeHaving(node, sourceScope);

      Scope outputScope = computeAndAssignOutputScope(node, scope, sourceScope);

      node.getFill()
          .ifPresent(
              fill -> {
                Scope fillScope = computeAndAssignFillScope(fill, sourceScope, outputScope);
                analyzeFill(fill, fillScope);
              });

      List<Expression> orderByExpressions = emptyList();
      Optional<Scope> orderByScope = Optional.empty();
      if (node.getOrderBy().isPresent()) {
        OrderBy orderBy = node.getOrderBy().get();
        orderByScope = Optional.of(computeAndAssignOrderByScope(orderBy, sourceScope, outputScope));

        orderByExpressions = analyzeOrderBy(node, orderBy.getSortItems(), orderByScope.get());

        if ((sourceScope.getOuterQueryParent().isPresent() || !isTopLevel)
            && !node.getLimit().isPresent()
            && !node.getOffset().isPresent()
            && !hasFillInParentScope) {
          // not the root scope and ORDER BY is ineffective
          analysis.markRedundantOrderBy(orderBy);
          warningCollector.add(
              new IoTDBWarning(REDUNDANT_ORDER_BY, "ORDER BY in subquery may have no effect"));
        }
      }
      analysis.setOrderByExpressions(node, orderByExpressions);

      if (node.getOffset().isPresent()) {
        analyzeOffset(node.getOffset().get(), outputScope);
      }

      if (node.getLimit().isPresent()) {
        boolean requiresOrderBy = analyzeLimit(node.getLimit().get(), outputScope);
        if (requiresOrderBy && !node.getOrderBy().isPresent()) {
          throw new SemanticException("FETCH FIRST WITH TIES clause requires ORDER BY");
        }
      }

      List<Expression> sourceExpressions = new ArrayList<>();
      analysis.getSelectExpressions(node).stream()
          .map(Analysis.SelectExpression::getExpression)
          .forEach(sourceExpressions::add);
      node.getHaving().ifPresent(sourceExpressions::add);

      for (WindowDefinition windowDefinition : node.getWindows()) {
        WindowSpecification window = windowDefinition.getWindow();
        sourceExpressions.addAll(window.getPartitionBy());
        getSortItemsFromOrderBy(window.getOrderBy()).stream()
            .map(SortItem::getSortKey)
            .forEach(sourceExpressions::add);
        if (window.getFrame().isPresent()) {
          WindowFrame frame = window.getFrame().get();
          frame.getStart().getValue().ifPresent(sourceExpressions::add);
          frame.getEnd().flatMap(FrameBound::getValue).ifPresent(sourceExpressions::add);
        }
      }

      analyzeAggregations(
          node, sourceScope, orderByScope, groupByAnalysis, sourceExpressions, orderByExpressions);
      analyzeWindowFunctionsAndMeasures(node, outputExpressions, orderByExpressions);

      if (analysis.isAggregation(node) && node.getOrderBy().isPresent()) {
        ImmutableList.Builder<Expression> aggregates =
            ImmutableList.<Expression>builder()
                .addAll(groupByAnalysis.getOriginalExpressions())
                .addAll(extractAggregateFunctions(orderByExpressions));

        analysis.setOrderByAggregates(node.getOrderBy().get(), aggregates.build());
      }

      if (node.getOrderBy().isPresent() && node.getSelect().isDistinct()) {
        verifySelectDistinct(
            node,
            orderByExpressions,
            outputExpressions,
            sourceScope,
            orderByScope.orElseThrow(() -> new NoSuchElementException("No value present")));
      }

      return outputScope;
    }
Visitor context represents local query scope (if exists). The invariant is that the local query
scopes hierarchy should always have outer query scope (if provided) as ancestor.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java