private void analyzeWhere(Node node, Scope scope, Expression predicate) {
      verifyNoAggregateWindowOrGroupingFunctions(predicate, "WHERE clause");

      // contains Columns, expand them and concat them
      if (containsColumns(predicate)) {
        ExpandColumnsVisitor visitor = new ExpandColumnsVisitor(null);
        List<Expression> expandedExpressions = visitor.process(predicate, scope);
        if (expandedExpressions.isEmpty()) {
          throw new IllegalStateException("There is at least one result of expanded");
        }
        if (expandedExpressions.size() >= 2) {
          predicate = new LogicalExpression(LogicalExpression.Operator.AND, expandedExpressions);
        } else {
          predicate = expandedExpressions.get(0);
        }
      }

      ExpressionAnalysis expressionAnalysis = analyzeExpression(predicate, scope);
      analysis.recordSubqueries(node, expressionAnalysis);

      Type predicateType = expressionAnalysis.getType(predicate);
      if (!predicateType.equals(BOOLEAN)) {
        //        if (!predicateType.equals(UNKNOWN)) {
        throw new SemanticException(
            String.format(
                "WHERE clause must evaluate to a boolean: actual type %s", predicateType));
        //        }
        // coerce null to boolean
        //        analysis.addCoercion(predicate, BOOLEAN, false);
      }

      analysis.setWhere(node, predicate);
    }
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