private static RelNode createTopProject(
          RelBuilder relBuilder,
          HiveLoptMultiJoin multiJoin,
          HiveLoptJoinTree joinTree,
          List<String> fieldNames) {
    List<RexNode> newProjExprs = new ArrayList<>();
    RexBuilder rexBuilder =
            multiJoin.getMultiJoinRel().getCluster().getRexBuilder();

    // create a projection on top of the joins, matching the original
    // join order
    final List<Integer> newJoinOrder = joinTree.getTreeOrder();
    int nJoinFactors = multiJoin.getNumJoinFactors();
    List<RelDataTypeField> fields = multiJoin.getMultiJoinFields();

    // create a mapping from each factor to its field offset in the join
    // ordering
    final Map<Integer, Integer> factorToOffsetMap = new HashMap<>();
    for (int pos = 0, fieldStart = 0; pos < nJoinFactors; pos++) {
      factorToOffsetMap.put(newJoinOrder.get(pos), fieldStart);
      fieldStart +=
              multiJoin.getNumFieldsInJoinFactor(newJoinOrder.get(pos));
    }

    for (int currFactor = 0; currFactor < nJoinFactors; currFactor++) {
      // if the factor is the right factor in a removable self-join,
      // then where possible, remap references to the right factor to
      // the corresponding reference in the left factor
      Integer leftFactor = null;
      if (multiJoin.isRightFactorInRemovableSelfJoin(currFactor)) {
        leftFactor = multiJoin.getOtherSelfJoinFactor(currFactor);
      }
      for (int fieldPos = 0;
           fieldPos < multiJoin.getNumFieldsInJoinFactor(currFactor);
           fieldPos++) {
        int newOffset = requireNonNull(factorToOffsetMap.get(currFactor),
                () -> "factorToOffsetMap.get(currFactor)") + fieldPos;
        if (leftFactor != null) {
          Integer leftOffset =
                  multiJoin.getRightColumnMapping(currFactor, fieldPos);
          if (leftOffset != null) {
            newOffset =
                    requireNonNull(factorToOffsetMap.get(leftFactor),
                            "factorToOffsetMap.get(leftFactor)") + leftOffset;
          }
        }
        newProjExprs.add(
                rexBuilder.makeInputRef(
                        fields.get(newProjExprs.size()).getType(),
                        newOffset));
      }
    }

    relBuilder.push(joinTree.getJoinTree());
    relBuilder.project(newProjExprs, fieldNames);

    // Place the post-join filter (if it exists) on top of the final
    // projection.
    RexNode postJoinFilter =
            multiJoin.getMultiJoinRel().getPostJoinFilter();
    if (postJoinFilter != null) {
      relBuilder.filter(postJoinFilter);
    }
    return relBuilder.build();
  }
Creates the topmost projection that will sit on top of the selected join
ordering. The projection needs to match the original join ordering. Also,
places any post-join filters on top of the project.

@param multiJoin join factors being optimized
@param joinTree selected join ordering
@param fieldNames field names corresponding to the projection expressions

@return created projection
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveLoptOptimizeJoinRule.java