private static @Nullable HiveLoptJoinTree createReplacementSemiJoin(
          RelBuilder relBuilder,
          HiveLoptMultiJoin multiJoin,
          HiveLoptSemiJoinOptimizer semiJoinOpt,
          @Nullable HiveLoptJoinTree factTree,
          int dimIdx,
          List<RexNode> filtersToAdd) {
    // if the current join tree doesn't contain the fact table, then
    // don't bother trying to create the replacement join just yet
    if (factTree == null) {
      return null;
    }

    int factIdx = requireNonNull(multiJoin.getJoinRemovalFactor(dimIdx),
            () -> "multiJoin.getJoinRemovalFactor(dimIdx) for " + dimIdx + ", " + multiJoin);
    final List<Integer> joinOrder = factTree.getTreeOrder();
    assert joinOrder.contains(factIdx);

    // figure out the position of the fact table in the current jointree
    int adjustment = 0;
    for (Integer factor : joinOrder) {
      if (factor == factIdx) {
        break;
      }
      adjustment += multiJoin.getNumFieldsInJoinFactor(factor);
    }

    // map the dimension keys to the corresponding keys from the fact
    // table, based on the fact table's position in the current jointree
    List<RelDataTypeField> dimFields =
            multiJoin.getJoinFactor(dimIdx).getRowType().getFieldList();
    int nDimFields = dimFields.size();
    Integer [] replacementKeys = new Integer[nDimFields];
    LogicalJoin semiJoin = multiJoin.getJoinRemovalSemiJoin(dimIdx);
    ImmutableIntList dimKeys = semiJoin.analyzeCondition().leftKeys;
    ImmutableIntList factKeys = semiJoin.analyzeCondition().rightKeys;
    for (int i = 0; i < dimKeys.size(); i++) {
      replacementKeys[dimKeys.get(i)] = factKeys.get(i) + adjustment;
    }

    return createReplacementJoin(
            relBuilder,
            multiJoin,
            semiJoinOpt,
            factTree,
            factIdx,
            dimIdx,
            dimKeys,
            replacementKeys,
            filtersToAdd);
  }
In the event that a dimension table does not need to be joined because of
a semijoin, this method creates a join tree that consists of a projection
on top of an existing join tree. The existing join tree must contain the
fact table in the semijoin that allows the dimension table to be removed.

<p>The projection created on top of the join tree mimics a join of the
fact and dimension tables. In order for the dimension table to have been
removed, the only fields referenced from the dimension table are its
dimension keys. Therefore, we can replace these dimension fields with the
fields corresponding to the semijoin keys from the fact table in the
projection.

@param multiJoin join factors being optimized
@param semiJoinOpt optimal semijoins for each factor
@param factTree existing join tree containing the fact table
@param dimIdx dimension table factor id
@param filtersToAdd filters remaining to be added; filters added to the
new join tree are removed from the list

@return created join tree or null if the corresponding fact table has not
been joined in yet
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveLoptOptimizeJoinRule.java