private float getSelectivityComplexTree(Operator<? extends OperatorDesc> op) {
      Operator<? extends OperatorDesc> multiParentOp = null;
      Operator<? extends OperatorDesc> currentOp = op;

      // TS-1      TS-2
      //  |          |
      // RS-1      RS-2
      //    \      /
      //      JOIN
      //        |
      //       FIL
      //        |
      //       RS-3
      //
      // For the above complex operator tree,
      // selectivity(JOIN) = selectivity(RS-1) * selectivity(RS-2) and
      // selectivity(RS-3) = numRows(RS-3)/numRows(JOIN) * selectivity(JOIN)
      while (multiParentOp == null) {
        if (op.getParentOperators().size() > 1) {
          multiParentOp = op;
        } else {
          op = op.getParentOperators().get(0);
        }
      }

      // No need for overflow checks, assume selectivity is always <= 1.0
      float selMultiParent = 1.0f;

      boolean isSelComputed = false;

      // if it is two way left outer or right outer join take selectivity only for
      // corresponding branch since only that branch will factor is the reduction
      if (multiParentOp instanceof JoinOperator) {
        JoinOperator jop = ((JoinOperator) multiParentOp);
        // check for two way join
        if (jop.getConf().getConds().length == 1) {
          isSelComputed = true;
          int type = jop.getConf().getCondsList().get(0).getType();
          if (jop.getConf().getJoinKeys()[0].length == 0 || type == JoinDesc.FULL_OUTER_JOIN) {
            // This is just a cartesian product or a full outer join, we will take the max
            float selMultiParentLeft = getSelectivitySimpleTree(multiParentOp.getParentOperators().get(0));
            float selMultiParentRight = getSelectivitySimpleTree(multiParentOp.getParentOperators().get(1));
            selMultiParent = Math.max(selMultiParentLeft, selMultiParentRight);
          } else {
            switch (type) {
            case JoinDesc.LEFT_OUTER_JOIN:
              selMultiParent = getSelectivitySimpleTree(multiParentOp.getParentOperators().get(0));
              break;
            case JoinDesc.RIGHT_OUTER_JOIN:
              selMultiParent = getSelectivitySimpleTree(multiParentOp.getParentOperators().get(1));
              break;
            default:
              // for rest of the join type we will take min of the reduction.
              float selMultiParentLeft = getSelectivitySimpleTree(multiParentOp.getParentOperators().get(0));
              float selMultiParentRight = getSelectivitySimpleTree(multiParentOp.getParentOperators().get(1));
              selMultiParent = Math.min(selMultiParentLeft, selMultiParentRight);
            }
          }
        }
      }

      if (!isSelComputed) {
        for (Operator<? extends OperatorDesc> parent : multiParentOp.getParentOperators()) {
          // In the above example, TS-1 -> RS-1 and TS-2 -> RS-2 are simple trees
          selMultiParent *= getSelectivitySimpleTree(parent);
        }
      }

      float selCurrOp = ((float) currentOp.getStatistics().getNumRows() /
          (float) multiParentOp.getStatistics().getNumRows()) * selMultiParent;

      return selCurrOp;
    }
Get cardinality of reduce sink operators.
@param csPK - ColStatistics for a single primary key
@param csFKs - ColStatistics for multiple foreign keys
ql/src/java/org/apache/hadoop/hive/ql/optimizer/stats/annotation/StatsRulesProcFactory.java