private Boolean scopeAwareComparison(Node left, Node right) {
    if (left instanceof Expression && right instanceof Expression) {
      Expression leftExpression = (Expression) left;
      Expression rightExpression = (Expression) right;
      if (analysis.isColumnReference(leftExpression)
          && analysis.isColumnReference(rightExpression)) {
        ResolvedField leftField = analysis.getResolvedField(leftExpression);
        ResolvedField rightField = analysis.getResolvedField(rightExpression);

        boolean leftFieldInSubqueryScope = leftField.getScope().hasOuterParent(queryScope);
        boolean rightFieldInSubqueryScope = rightField.getScope().hasOuterParent(queryScope);

        // For subqueries of the query associated with the current expression, compare by syntax
        if (leftFieldInSubqueryScope && rightFieldInSubqueryScope) {
          return treeEqual(
              leftExpression,
              rightExpression,
              CanonicalizationAware::canonicalizationAwareComparison);
        }
        // For references that come from the current query scope or an outer scope of the current
        // expression, compare by resolved field
        if (!leftFieldInSubqueryScope && !rightFieldInSubqueryScope) {
          return leftField.getFieldId().equals(rightField.getFieldId());
        }
        // References come from different scopes
        return false;
      }
      if (leftExpression instanceof Identifier && rightExpression instanceof Identifier) {
        return treeEqual(
            leftExpression,
            rightExpression,
            CanonicalizationAware::canonicalizationAwareComparison);
      }
    }

    if (!left.shallowEquals(right)) {
      return false;
    }

    return null;
  }
Syntactic comparison of Nodes with special handling of column references.

<p>When both compared Nodes are column references, the following rules apply:

<p>1. If both columns belong to scopes associated with subqueries of the current query, they
are compared by syntax (that is, textually, respecting the canonicalization rules).

<p>2. If none of the columns belongs to some subquery scope, that is, either they belong to the
current query scope, or some outer scope, they are compared by resolved field.

<p>3. If only one of the columns belongs to some subquery scope, they are not equal.

<p>Note: it'd appear that hash() and equal() are inconsistent with each other in the case that:
- left.hasOuterParent(...) == true and right.hasOuterParent(...) == false -
leftField.getFieldId().equals(rightField.getFieldId()) == true Both fields would seem to have
different hashes but be equal to each other. However, this cannot happen because we *require*
that both expressions being compared be rooted in the same "query scope" (i.e., sub-scopes that
are local to each other) -- see ScopeAwareKey.equals(). If both fields have the same field id,
by definition they will produce the same result for hasOuterParent().
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ScopeAware.java