static InnerJoinPushDownResult processInnerJoin(
      Metadata metadata,
      Expression inheritedPredicate,
      Expression leftEffectivePredicate,
      Expression rightEffectivePredicate,
      Expression joinPredicate,
      Collection<Symbol> leftSymbols,
      Collection<Symbol> rightSymbols) {
    checkArgument(
        leftSymbols.containsAll(extractUnique(leftEffectivePredicate)),
        "leftEffectivePredicate must only contain symbols from leftSymbols");
    checkArgument(
        rightSymbols.containsAll(extractUnique(rightEffectivePredicate)),
        "rightEffectivePredicate must only contain symbols from rightSymbols");

    ImmutableList.Builder<Expression> leftPushDownConjuncts = ImmutableList.builder();
    ImmutableList.Builder<Expression> rightPushDownConjuncts = ImmutableList.builder();
    ImmutableList.Builder<Expression> joinConjuncts = ImmutableList.builder();

    // Strip out non-deterministic conjuncts
    extractConjuncts(inheritedPredicate).stream()
        .filter(deterministic -> !isDeterministic(deterministic))
        .forEach(joinConjuncts::add);
    inheritedPredicate = filterDeterministicConjuncts(inheritedPredicate);

    extractConjuncts(joinPredicate).stream()
        .filter(expression -> !isDeterministic(expression))
        .forEach(joinConjuncts::add);
    joinPredicate = filterDeterministicConjuncts(joinPredicate);

    leftEffectivePredicate = filterDeterministicConjuncts(leftEffectivePredicate);
    rightEffectivePredicate = filterDeterministicConjuncts(rightEffectivePredicate);

    ImmutableSet<Symbol> leftScope = ImmutableSet.copyOf(leftSymbols);
    ImmutableSet<Symbol> rightScope = ImmutableSet.copyOf(rightSymbols);

    // Generate equality inferences
    EqualityInference allInference =
        new EqualityInference(
            metadata,
            inheritedPredicate,
            leftEffectivePredicate,
            rightEffectivePredicate,
            joinPredicate);
    EqualityInference allInferenceWithoutLeftInferred =
        new EqualityInference(metadata, inheritedPredicate, rightEffectivePredicate, joinPredicate);
    EqualityInference allInferenceWithoutRightInferred =
        new EqualityInference(metadata, inheritedPredicate, leftEffectivePredicate, joinPredicate);

    // Add equalities from the inference back in
    leftPushDownConjuncts.addAll(
        allInferenceWithoutLeftInferred
            .generateEqualitiesPartitionedBy(leftScope)
            .getScopeEqualities());
    rightPushDownConjuncts.addAll(
        allInferenceWithoutRightInferred
            .generateEqualitiesPartitionedBy(rightScope)
            .getScopeEqualities());
    joinConjuncts.addAll(
        allInference
            .generateEqualitiesPartitionedBy(leftScope)
            .getScopeStraddlingEqualities()); // scope straddling equalities get dropped in as
    // part of the join predicate

    // Sort through conjuncts in inheritedPredicate that were not used for inference
    EqualityInference.nonInferrableConjuncts(metadata, inheritedPredicate)
        .forEach(
            conjunct -> {
              Expression leftRewrittenConjunct = allInference.rewrite(conjunct, leftScope);
              if (leftRewrittenConjunct != null) {
                leftPushDownConjuncts.add(leftRewrittenConjunct);
              }

              Expression rightRewrittenConjunct = allInference.rewrite(conjunct, rightScope);
              if (rightRewrittenConjunct != null) {
                rightPushDownConjuncts.add(rightRewrittenConjunct);
              }

              // Drop predicate after join only if unable to push down to either side
              if (leftRewrittenConjunct == null && rightRewrittenConjunct == null) {
                joinConjuncts.add(conjunct);
              }
            });

    // See if we can push the right effective predicate to the left side
    EqualityInference.nonInferrableConjuncts(metadata, rightEffectivePredicate)
        .map(conjunct -> allInference.rewrite(conjunct, leftScope))
        .filter(Objects::nonNull)
        .forEach(leftPushDownConjuncts::add);

    // See if we can push the left effective predicate to the right side
    EqualityInference.nonInferrableConjuncts(metadata, leftEffectivePredicate)
        .map(conjunct -> allInference.rewrite(conjunct, rightScope))
        .filter(Objects::nonNull)
        .forEach(rightPushDownConjuncts::add);

    // See if we can push any parts of the join predicates to either side
    EqualityInference.nonInferrableConjuncts(metadata, joinPredicate)
        .forEach(
            conjunct -> {
              Expression leftRewritten = allInference.rewrite(conjunct, leftScope);
              if (leftRewritten != null) {
                leftPushDownConjuncts.add(leftRewritten);
              }

              Expression rightRewritten = allInference.rewrite(conjunct, rightScope);
              if (rightRewritten != null) {
                rightPushDownConjuncts.add(rightRewritten);
              }

              if (leftRewritten == null && rightRewritten == null) {
                joinConjuncts.add(conjunct);
              }
            });

    return new InnerJoinPushDownResult(
        combineConjuncts(leftPushDownConjuncts.build()),
        combineConjuncts(rightPushDownConjuncts.build()),
        combineConjuncts(joinConjuncts.build()),
        TRUE_LITERAL);
  }
If the expression is EQUAL ComparisonExpression

@return true if the expression is EQUAL ComparisonExpression and leftSymbols contains
    expression.getLeft() and rightSymbols contains expression.getRight().
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/JoinUtils.java