private static boolean areMergeableExcludeSemijoinsExtendedCheck(ParseContext pctx, SharedWorkOptimizerCache optimizerCache,
      TableScanOperator tsOp1, TableScanOperator tsOp2) throws SemanticException {
    // We remove RS-based SJs from consideration, then we compare
    List<Operator<?>> dppsOp1 = new ArrayList<>(optimizerCache.tableScanToDPPSource.get(tsOp1));
    boolean removedDppOp1 = false;
    List<ReduceSinkOperator> rsOpsSemijoin1 = new ArrayList<>();
    List<Operator<?>> dppsOp2 = new ArrayList<>(optimizerCache.tableScanToDPPSource.get(tsOp2));
    boolean removedDppOp2 = false;
    List<ReduceSinkOperator> rsOpsSemijoin2 = new ArrayList<>();
    for (int i = 0; i < dppsOp1.size(); i++) {
      Operator<?> op = dppsOp1.get(i);
      if (op instanceof ReduceSinkOperator) {
        ReduceSinkOperator semijoinRSOp = (ReduceSinkOperator) op;
        if (pctx.getRsToSemiJoinBranchInfo().get(semijoinRSOp).getIsHint()) {
          // This is a hint, we should keep it, hence we bail out
          return false;
        }
        rsOpsSemijoin1.add(semijoinRSOp);
        dppsOp1.remove(i);
        removedDppOp1 = true;
      }
    }
    for (int i = 0; i < dppsOp2.size(); i++) {
      Operator<?> op = dppsOp2.get(i);
      if (op instanceof ReduceSinkOperator) {
        ReduceSinkOperator semijoinRSOp = (ReduceSinkOperator) op;
        if (pctx.getRsToSemiJoinBranchInfo().get(semijoinRSOp).getIsHint()) {
          // This is a hint, we should keep it, hence we bail out
          return false;
        }
        rsOpsSemijoin2.add(semijoinRSOp);
        dppsOp2.remove(i);
        removedDppOp2 = true;
      }
    }
    if (removedDppOp1 && removedDppOp2) {
      // TODO: We do not merge, since currently we only merge when one of the TS operators
      // are not targetted by a SJ edge
      return false;
    }
    if (!removedDppOp1 && !removedDppOp2) {
      // None of them are targetted by a SJ, we skip them
      return false;
    }
    if (dppsOp1.size() != dppsOp2.size()) {
      // We cannot merge, we move to the next couple
      return false;
    }
    // Check if DPP branches are equal
    boolean equalBranches = true;
    BitSet bs = new BitSet();
    for (int i = 0; i < dppsOp1.size(); i++) {
      Operator<?> dppOp1 = dppsOp1.get(i);
      for (int j = 0; j < dppsOp2.size(); j++) {
        if (!bs.get(j)) {
          // If not visited yet
          Operator<?> dppOp2 = dppsOp2.get(j);
          if (compareAndGatherOps(pctx, optimizerCache, dppOp1, dppOp2) != null) {
            // The DPP operator/branch are equal
            bs.set(j);
            break;
          }
        }
      }
      if (bs.cardinality() < i + 1) {
        // We cannot merge, we move to the next group
        equalBranches = false;
        break;
      }
    }
    if (!equalBranches) {
      // Skip
      return false;
    }

    // We reached here, other DPP is the same, these two could potentially be merged.
    // Hence, we perform the last check. To do this, we remove the SJ operators,
    // but we remember their position in the plan. After that, we will reintroduce
    // the SJ operator. If the checks were valid, we will merge and remove the semijoin.
    // If the rest of tests to merge do not pass, we will abort the shared scan optimization
    // and we are done
    TableScanOperator targetTSOp;
    List<ReduceSinkOperator> semijoinRsOps;
    List<SemiJoinBranchInfo> sjBranches = new ArrayList<>();
    if (removedDppOp1) {
      targetTSOp = tsOp1;
      semijoinRsOps = rsOpsSemijoin1;
    } else {
      targetTSOp = tsOp2;
      semijoinRsOps = rsOpsSemijoin2;
    }
    optimizerCache.tableScanToDPPSource.get(targetTSOp).removeAll(semijoinRsOps);
    for (ReduceSinkOperator rsOp : semijoinRsOps) {
      sjBranches.add(pctx.getRsToSemiJoinBranchInfo().remove(rsOp));
    }

    boolean validMerge = validPreConditions(pctx, optimizerCache,
        extractSharedOptimizationInfoForRoot(pctx, optimizerCache, tsOp1, tsOp2, true, true));

    if (validMerge) {
      // We are going to merge, hence we remove the semijoins completely
      for (ReduceSinkOperator semijoinRsOp : semijoinRsOps) {
        Operator<?> branchOp = GenTezUtils.removeBranch(semijoinRsOp);
        while (branchOp != null) {
          optimizerCache.removeOp(branchOp);
          branchOp = branchOp.getNumChild() > 0 ?
              branchOp.getChildOperators().get(0) : null;
        }
        GenTezUtils.removeSemiJoinOperator(pctx, semijoinRsOp, targetTSOp);
      }
    } else {
      // Otherwise, the put the semijoins back in the auxiliary data structures
      optimizerCache.tableScanToDPPSource.get(targetTSOp).addAll(semijoinRsOps);
      for (int i = 0; i < semijoinRsOps.size(); i++) {
        pctx.getRsToSemiJoinBranchInfo().put(semijoinRsOps.get(i), sjBranches.get(i));
      }
    }
    return validMerge;
  }
Orders TS operators in decreasing order by "weight".
ql/src/java/org/apache/hadoop/hive/ql/optimizer/SharedWorkOptimizer.java