protected Scope visitPatternRecognitionRelation(
        PatternRecognitionRelation relation, Optional<Scope> scope) {
      Scope inputScope = process(relation.getInput(), scope);

      // check that input table column names are not ambiguous
      // Note: This check is not compliant with SQL identifier semantics. Quoted identifiers should
      // have different comparison rules than unquoted identifiers.
      // However, field names do not contain the information about quotation, and so every
      // comparison is case-insensitive. For example, if there are fields named
      // 'a' and 'A' (quoted), they should be considered non-ambiguous. However, their names will be
      // compared case-insensitive and will cause failure as ambiguous.
      Set<String> inputNames = new HashSet<>();
      for (Field field : inputScope.getRelationType().getAllFields()) {
        field
            .getName()
            .ifPresent(
                name -> {
                  if (!inputNames.add(name.toUpperCase(ENGLISH))) {
                    throw new SemanticException(
                        String.format("ambiguous column: %s in row pattern input relation", name));
                  }
                });
      }

      // analyze PARTITION BY
      for (Expression expression : relation.getPartitionBy()) {
        // The PARTITION BY clause is a list of columns of the row pattern input table.
        validateAndGetInputField(expression, inputScope);
        Type type = analyzeExpression(expression, inputScope).getType(expression);
        if (!type.isComparable()) {
          throw new SemanticException(
              String.format(
                  "%s is not comparable, and therefore cannot be used in PARTITION BY", type));
        }
      }

      // analyze ORDER BY
      for (SortItem sortItem : getSortItemsFromOrderBy(relation.getOrderBy())) {
        // The ORDER BY clause is a list of columns of the row pattern input table.
        Expression expression = sortItem.getSortKey();
        validateAndGetInputField(expression, inputScope);
        Type type = analyzeExpression(expression, inputScope).getType(sortItem.getSortKey());
        if (!type.isOrderable()) {
          throw new SemanticException(
              String.format("%s is not orderable, and therefore cannot be used in ORDER BY", type));
        }
      }

      // analyze pattern recognition clauses
      PatternRecognitionAnalysis patternRecognitionAnalysis =
          PatternRecognitionAnalyzer.analyze(
              relation.getSubsets(),
              relation.getVariableDefinitions(),
              relation.getMeasures(),
              relation.getPattern(),
              relation.getAfterMatchSkipTo());

      relation
          .getAfterMatchSkipTo()
          .flatMap(SkipTo::getIdentifier)
          .ifPresent(label -> analysis.addResolvedLabel(label, label.getCanonicalValue()));

      for (SubsetDefinition subset : relation.getSubsets()) {
        analysis.addResolvedLabel(subset.getName(), subset.getName().getCanonicalValue());
        analysis.addSubsetLabels(
            subset,
            subset.getIdentifiers().stream()
                .map(Identifier::getCanonicalValue)
                .collect(Collectors.toSet()));
      }

      analysis.setUndefinedLabels(
          relation.getPattern(), patternRecognitionAnalysis.getUndefinedLabels());
      analysis.setRanges(patternRecognitionAnalysis.getRanges());

      PatternRecognitionAnalyzer.validatePatternExclusions(
          relation.getRowsPerMatch(), relation.getPattern());

      // Notes on potential name ambiguity between pattern labels and other identifiers:
      // Labels are allowed in expressions of MEASURES and DEFINE clauses. In those expressions,
      // qualifying column names with table name is not allowed.
      // Theoretically, user might define pattern label "T" where input table name was "T". Then a
      // dereference "T.column" would refer to:
      // - input table's column, if it was in PARTITION BY or ORDER BY clause,
      // - subset of rows matched with label "T", if it was in MEASURES or DEFINE clause.
      // There could be a check to catch such non-intuitive situation and produce a warning.
      // Similarly, it is possible to define pattern label with the same name as some input column.
      // However, this causes no ambiguity, as labels can only
      // appear as column name's prefix, and column names in pattern recognition context cannot be
      // dereferenced.

      // analyze expressions in MEASURES and DEFINE (with set of all labels passed as context)
      for (VariableDefinition variableDefinition : relation.getVariableDefinitions()) {
        Expression expression = variableDefinition.getExpression();
        ExpressionAnalysis expressionAnalysis =
            analyzePatternRecognitionExpression(
                expression, inputScope, patternRecognitionAnalysis.getAllLabels());
        analysis.recordSubqueries(relation, expressionAnalysis);
        analysis.addResolvedLabel(
            variableDefinition.getName(), variableDefinition.getName().getCanonicalValue());
        Type type = expressionAnalysis.getType(expression);
        if (!type.equals(BOOLEAN)) {
          throw new SemanticException(
              String.format("Expression defining a label must be boolean (actual type: %s)", type));
        }
      }
      ImmutableMap.Builder<NodeRef<Node>, Type> measureTypesBuilder = ImmutableMap.builder();
      for (MeasureDefinition measureDefinition : relation.getMeasures()) {
        Expression expression = measureDefinition.getExpression();
        ExpressionAnalysis expressionAnalysis =
            analyzePatternRecognitionExpression(
                expression, inputScope, patternRecognitionAnalysis.getAllLabels());
        analysis.recordSubqueries(relation, expressionAnalysis);
        analysis.addResolvedLabel(
            measureDefinition.getName(), measureDefinition.getName().getCanonicalValue());
        measureTypesBuilder.put(NodeRef.of(expression), expressionAnalysis.getType(expression));
      }
      Map<NodeRef<Node>, Type> measureTypes = measureTypesBuilder.buildOrThrow();

      // create output scope
      // ONE ROW PER MATCH: PARTITION BY columns, then MEASURES columns in order of declaration
      // ALL ROWS PER MATCH: PARTITION BY columns, ORDER BY columns, MEASURES columns, then any
      // remaining input table columns in order of declaration
      // Note: row pattern input table name should not be exposed on output
      PatternRecognitionRelation.RowsPerMatch rowsPerMatch = relation.getRowsPerMatch().orElse(ONE);
      boolean oneRowPerMatch = rowsPerMatch == ONE;

      ImmutableSet.Builder<Field> inputFieldsOnOutputBuilder = ImmutableSet.builder();
      ImmutableList.Builder<Field> outputFieldsBuilder = ImmutableList.builder();

      for (Expression expression : relation.getPartitionBy()) {
        Field inputField = validateAndGetInputField(expression, inputScope);
        outputFieldsBuilder.add(unqualifiedVisible(inputField));
        inputFieldsOnOutputBuilder.add(inputField);
      }

      if (!oneRowPerMatch) {
        for (SortItem sortItem : getSortItemsFromOrderBy(relation.getOrderBy())) {
          Field inputField = validateAndGetInputField(sortItem.getSortKey(), inputScope);
          outputFieldsBuilder.add(unqualifiedVisible(inputField));
          inputFieldsOnOutputBuilder.add(
              inputField); // might have duplicates (ORDER BY a ASC, a DESC)
        }
      }

      for (MeasureDefinition measureDefinition : relation.getMeasures()) {
        outputFieldsBuilder.add(
            Field.newUnqualified(
                measureDefinition.getName().getValue(),
                measureTypes.get(NodeRef.of(measureDefinition.getExpression())),
                TsTableColumnCategory.FIELD));
      }

      if (!oneRowPerMatch) {
        Set<Field> inputFieldsOnOutput = inputFieldsOnOutputBuilder.build();
        for (Field inputField : inputScope.getRelationType().getAllFields()) {
          if (!inputFieldsOnOutput.contains(inputField)) {
            outputFieldsBuilder.add(unqualified(inputField));
          }
        }
      }

      // pattern recognition output must have at least 1 column
      List<Field> outputFields = outputFieldsBuilder.build();
      if (outputFields.isEmpty()) {
        throw new SemanticException("pattern recognition output table has no columns");
      }

      return createAndAssignScope(relation, scope, outputFields);
    }
Check if there is Columns function in expression, and verify they are same if there are multi
Column functions.

@param expression input expression
@return if there is Columns function in expression
@throws SemanticException if there are multi Columns functions but different
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java