@Override
  protected Boolean visitCast(Cast actual, Expression expectedExpression) {
    if (!(expectedExpression instanceof Cast)) {
      return false;
    }
    Cast expected = (Cast) expectedExpression;

    // TODO: hack!! The type in Cast is an AST structure, subject to case-sensitivity and quoting
    // rules
    // Here we're trying to verify its IR counterpart, but the plan testing framework goes directly
    // from SQL text -> IR-like expressions without doing all the proper canonicalizations. So we
    // cheat
    // here and normalize everything to the same case before comparing
    if (!actual.getType().toString().equalsIgnoreCase(expected.getType().toString())) {
      return false;
    }

    return process(actual.getExpression(), expected.getExpression());
  }
Expression visitor which verifies if given expression (actual) is matching other expression given
as context (expected). Visitor returns true if plans match to each other.

<p>Note that actual expression is using real name references (table columns etc) while expected
expression is using symbol aliases. Given symbol alias can point only to one real name reference.

<p>Example:

<pre>
NOT (orderkey = 3 AND custkey = 3 AND orderkey < 10)
</pre>

will match to:

<pre>
NOT (X = 3 AND Y = 3 AND X < 10)
</pre>

, but will not match to:

<pre>
NOT (X = 3 AND Y = 3 AND Z < 10)
</pre>

nor to

<pre>
NOT (X = 3 AND X = 3 AND X < 10)
</pre>
iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionVerifier.java