private static @Nullable HiveLoptJoinTree addFactorToTree(
          RelMetadataQuery mq,
          RelBuilder relBuilder,
          HiveLoptMultiJoin multiJoin,
          HiveLoptSemiJoinOptimizer semiJoinOpt,
          @Nullable HiveLoptJoinTree joinTree,
          int factorToAdd,
          BitSet factorsNeeded,
          List<RexNode> filtersToAdd,
          boolean selfJoin) {

    // if the factor corresponds to the null generating factor in an outer
    // join that can be removed, then create a replacement join
    if (multiJoin.isRemovableOuterJoinFactor(factorToAdd)) {
      return createReplacementJoin(
              relBuilder,
              multiJoin,
              semiJoinOpt,
              requireNonNull(joinTree, "joinTree"),
              -1,
              factorToAdd,
              ImmutableIntList.of(),
              null,
              filtersToAdd);
    }

    // if the factor corresponds to a dimension table whose join we
    // can remove, create a replacement join if the corresponding fact
    // table is in the current join tree
    if (multiJoin.getJoinRemovalFactor(factorToAdd) != null) {
      return createReplacementSemiJoin(
              relBuilder,
              multiJoin,
              semiJoinOpt,
              joinTree,
              factorToAdd,
              filtersToAdd);
    }

    // if this is the first factor in the tree, create a join tree with
    // the single factor
    if (joinTree == null) {
      return new HiveLoptJoinTree(
              semiJoinOpt.getChosenSemiJoin(factorToAdd),
              factorToAdd);
    }

    // Create a temporary copy of the filter list as we may need the
    // original list to pass into addToTop().  However, if no tree was
    // created by addToTop() because the factor being added is part of
    // a self-join, then pass the original filter list so the added
    // filters will still be removed from the list.
    final List<RexNode> tmpFilters = new ArrayList<>(filtersToAdd);
    HiveLoptJoinTree topTree =
            addToTop(
                    mq,
                    relBuilder,
                    multiJoin,
                    semiJoinOpt,
                    joinTree,
                    factorToAdd,
                    filtersToAdd,
                    selfJoin);
    HiveLoptJoinTree pushDownTree =
            pushDownFactor(
                    mq,
                    relBuilder,
                    multiJoin,
                    semiJoinOpt,
                    joinTree,
                    factorToAdd,
                    factorsNeeded,
                    (topTree == null) ? filtersToAdd : tmpFilters,
                    selfJoin);

    // pick the lower cost option, and replace the join ordering with
    // the ordering associated with the best option
    HiveLoptJoinTree bestTree;
    RelOptCost costPushDown = null;
    RelOptCost costTop = null;
    if (pushDownTree != null) {
      costPushDown = mq.getCumulativeCost(pushDownTree.getJoinTree());
    }
    if (topTree != null) {
      costTop = mq.getCumulativeCost(topTree.getJoinTree());
    }

    if (pushDownTree == null) {
      bestTree = topTree;
    } else if (topTree == null) {
      bestTree = pushDownTree;
    } else {
      requireNonNull(costPushDown, "costPushDown");
      requireNonNull(costTop, "costTop");
      if (costPushDown.isEqWithEpsilon(costTop)) {
        // if both plans cost the same (with an allowable round-off
        // margin of error), favor the one that passes
        // around the wider rows further up in the tree
        if (rowWidthCost(pushDownTree.getJoinTree())
                < rowWidthCost(topTree.getJoinTree())) {
          bestTree = pushDownTree;
        } else {
          bestTree = topTree;
        }
      } else if (costPushDown.isLt(costTop)) {
        bestTree = pushDownTree;
      } else {
        bestTree = topTree;
      }
    }

    return bestTree;
  }
Adds a new factor into the current join tree. The factor is either pushed
down into one of the subtrees of the join recursively, or it is added to
the top of the current tree, whichever yields a better ordering.

@param multiJoin join factors being optimized
@param semiJoinOpt optimal semijoins for each factor
@param joinTree current join tree
@param factorToAdd new factor to be added
@param factorsNeeded factors that must precede the factor to be added
@param filtersToAdd filters remaining to be added; filters added to the
new join tree are removed from the list
@param selfJoin true if the join being created is a self-join that's
removable

@return optimal join tree with the new factor added if it is possible to
add the factor; otherwise, null is returned
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveLoptOptimizeJoinRule.java