public static Operator<? extends OperatorDesc> findSourceRS(Operator<?> start, List<ExprNodeDesc> exprs) {
    Operator currRS = null; //keep track of the RS
    if (start instanceof ReduceSinkOperator) {
      currRS = start;
    }

    if (start instanceof UnionOperator) {
      //Union keeps the schema same but can change the cardinality, therefore we don't want to backtrack further
      // into Union
      return currRS;
    }

    List<Operator<? extends OperatorDesc>> parents = start.getParentOperators();
    if (parents == null | parents.isEmpty()) {
      // reached end e.g. TS operator
      return null;
    }

    Operator<? extends OperatorDesc> nextOp = null;
    List<ExprNodeDesc> backtrackedExprs = null;
    for (int i = 0; i < parents.size(); i++) {
      backtrackedExprs = backtrackAll(exprs, start, parents.get(i));
      if (areBacktrackedExprsCompatible(exprs, backtrackedExprs)) {
        nextOp = parents.get(i);
        break;
      }
    }
    if (nextOp != null) {
      Operator<? extends OperatorDesc> nextRS = findSourceRS(nextOp, backtrackedExprs);
      if (nextRS != null) {
        currRS = nextRS;
      }
    }
    return currRS;
  }
This method backtracks the given expressions to the source RS. Note that expressions could
further be backtracked to e.g. table source, but we are interested in RS only because this
is used to estimate number of rows for group by and estimation will be better at RS since all
the filters etc will have already been applied
@param start
@param exprs
@return null if RS is not found
ql/src/java/org/apache/hadoop/hive/ql/exec/OperatorUtils.java