private OptionalInt scopeAwareHash(Node node) {
    if (node instanceof Expression) {
      Expression expression = (Expression) node;
      if (analysis.isColumnReference(expression)) {
        ResolvedField field = analysis.getResolvedField(expression);

        Scope resolvedScope = field.getScope();
        if (resolvedScope.hasOuterParent(queryScope)) {
          return OptionalInt.of(
              treeHash(expression, CanonicalizationAware::canonicalizationAwareHash));
        }

        return OptionalInt.of(field.getFieldId().hashCode());
      }
      if (expression instanceof Identifier) {
        return OptionalInt.of(
            treeHash(expression, CanonicalizationAware::canonicalizationAwareHash));
      }
      if (node.getChildren().isEmpty()) {
        // Calculate shallow hash since node doesn't have any children
        return OptionalInt.of(expression.hashCode());
      }
    }

    return OptionalInt.empty();
  }
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