private static void findBestOrderings(
          RelMetadataQuery mq,
          RelBuilder relBuilder,
          HiveLoptMultiJoin multiJoin,
          HiveLoptSemiJoinOptimizer semiJoinOpt,
          RelOptRuleCall call) {
    final List<RelNode> plans = new ArrayList<>();

    final List<String> fieldNames =
            multiJoin.getMultiJoinRel().getRowType().getFieldNames();

    // generate the N join orderings
    for (int i = 0; i < multiJoin.getNumJoinFactors(); i++) {
      // first factor cannot be null generating
      if (multiJoin.isNullGenerating(i)) {
        continue;
      }
      HiveLoptJoinTree joinTree =
              createOrdering(
                      mq,
                      relBuilder,
                      multiJoin,
                      semiJoinOpt,
                      i);
      if (joinTree == null) {
        continue;
      }

      RelNode newProject =
              createTopProject(call.builder(), multiJoin, joinTree, fieldNames);
      plans.add(newProject);
    }

    // transform the selected plans; note that we wait till then the end to
    // transform everything so any intermediate RelNodes we create are not
    // converted to RelSubsets The HEP planner will choose the join subtree
    // with the best cumulative cost. Volcano planner keeps the alternative
    // join subtrees and cost the final plan to pick the best one.
    for (RelNode plan : plans) {
      call.transformTo(plan);
    }
  }
Generates N optimal join orderings. Each ordering contains each factor as
the first factor in the ordering.

@param multiJoin join factors being optimized
@param semiJoinOpt optimal semijoins for each factor
@param call RelOptRuleCall associated with this rule
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveLoptOptimizeJoinRule.java