private void markSemiJoinForDPP(OptimizeTezProcContext procCtx)
          throws SemanticException {
    // Stores the Tablescan operators processed to avoid redoing them.
    Map<ReduceSinkOperator, SemiJoinBranchInfo> map = procCtx.parseContext.getRsToSemiJoinBranchInfo();

    for (ReduceSinkOperator rs : map.keySet()) {
      SemiJoinBranchInfo sjInfo = map.get(rs);
      TableScanOperator ts = sjInfo.getTsOp();

      if (sjInfo.getIsHint() || !sjInfo.getShouldRemove()) {
        continue;
      }

      // A TS can have multiple branches due to DPP Or Semijoin Opt.
      // Use DFS to traverse all the branches until RS or DPP is hit.
      Deque<Operator<?>> deque = new LinkedList<>();
      deque.add(ts);
      while (!deque.isEmpty()) {
        Operator<?> op = deque.pollLast();
        if (op instanceof AppMasterEventOperator &&
                ((AppMasterEventOperator) op).getConf() instanceof DynamicPruningEventDesc) {
          // DPP. Now look up nDVs on both sides to see the selectivity.
          // <Parent Ops>-SEL-GB1-RS1-GB2-RS2
          SelectOperator selOp = OperatorUtils.ancestor(rs, SelectOperator.class, 0, 0, 0, 0);

          try {
            // Get nDVs on Semijoin edge side
            Statistics stats = selOp.getStatistics();
            if (stats == null) {
              // No stats found on semijoin edge, do nothing
              break;
            }
            String selCol = ExprNodeDescUtils.extractColName(
                    selOp.getConf().getColList().get(0));
            ColStatistics colStatisticsSJ = stats
                    .getColumnStatisticsFromColName(selCol);
            if (colStatisticsSJ == null) {
              // No column stats found for semijoin edge
              break;
            }
            long nDVs = colStatisticsSJ.getCountDistint();
            if (nDVs > 0) {
              // Lookup nDVs on TS side.
              RuntimeValuesInfo rti = procCtx.parseContext
                      .getRsToRuntimeValuesInfoMap().get(rs);
              // TODO Handle multi column semi-joins as part of HIVE-23934
              ExprNodeDesc tsExpr = rti.getTargetColumns().get(0);
              FilterOperator fil = (FilterOperator) (ts.getChildOperators().get(0));
              Statistics filStats = fil.getStatistics();
              if (filStats == null) {
                // No stats found on target, do nothing
                break;
              }
              String colName = ExprNodeDescUtils.extractColName(tsExpr);
              ColStatistics colStatisticsTarget = filStats
                      .getColumnStatisticsFromColName(colName);
              if (colStatisticsTarget == null) {
                // No column stats found on target
                break;
              }
              long nDVsOfTS = colStatisticsTarget.getCountDistint();
              double nDVsOfTSFactored = nDVsOfTS * procCtx.conf.getFloatVar(
                      ConfVars.TEZ_DYNAMIC_SEMIJOIN_REDUCTION_FOR_DPP_FACTOR);
              if ((long)nDVsOfTSFactored > nDVs) {
                if (LOG.isDebugEnabled()) {
                  LOG.debug("nDVs = " + nDVs + ", nDVsOfTS = " + nDVsOfTS + " and nDVsOfTSFactored = " + nDVsOfTSFactored
                  + "Adding semijoin branch from ReduceSink " + rs + " to TS " + sjInfo.getTsOp());
                }
                sjInfo.setShouldRemove(false);
              }
            }
          } catch (NullPointerException e) {
            // Do nothing
            if (LOG.isDebugEnabled()) {
              LOG.debug("Caught NPE in markSemiJoinForDPP from ReduceSink " + rs + " to TS " + sjInfo.getTsOp());
            }
          }
          break;
        }
        if (op instanceof TerminalOperator) {
          // Done with this branch
          continue;
        }
        deque.addAll(op.getChildOperators());
      }
    }
  }
Internal class to encapsulate information needed to evaluate stats
about a SJ that will be kept in the tree.
ql/src/java/org/apache/hadoop/hive/ql/parse/TezCompiler.java