private static Double getJoinDistinctRowCount(RelMetadataQuery mq,
      RelNode joinRel, JoinRelType joinType, ImmutableBitSet groupKey,
      RexNode predicate, boolean useMaxNdv) {
    Double distRowCount = null;
    ImmutableBitSet.Builder leftMask = ImmutableBitSet.builder();
    ImmutableBitSet.Builder rightMask = ImmutableBitSet.builder();
    RelNode left = joinRel.getInputs().get(0);
    RelNode right = joinRel.getInputs().get(1);

    RelMdUtil.setLeftRightBitmaps(
        groupKey,
        leftMask,
        rightMask,
        left.getRowType().getFieldCount());

    // determine which filters apply to the left vs right
    RexNode leftPred = null;
    RexNode rightPred = null;
    if (predicate != null) {
      final List<RexNode> leftFilters = new ArrayList<>();
      final List<RexNode> rightFilters = new ArrayList<>();
      final List<RexNode> joinFilters = new ArrayList<>();
      final List<RexNode> predList = RelOptUtil.conjunctions(predicate);

      RelOptUtil.classifyFilters(
          joinRel,
          predList,
          joinType,
          joinType == JoinRelType.INNER,
          !joinType.generatesNullsOnLeft(),
          !joinType.generatesNullsOnRight(),
          joinFilters,
          leftFilters,
          rightFilters);

      RexBuilder rexBuilder = joinRel.getCluster().getRexBuilder();
      leftPred =
          RexUtil.composeConjunction(rexBuilder, leftFilters, true);
      rightPred =
          RexUtil.composeConjunction(rexBuilder, rightFilters, true);
    }

    Double leftDistRowCount = mq.getDistinctRowCount(left, leftMask.build(), leftPred);
    Double rightDistRowCount = mq.getDistinctRowCount(right, rightMask.build(), rightPred);
    if (leftDistRowCount != null && rightDistRowCount != null) {
      if (useMaxNdv) {
        distRowCount = Math.max(leftDistRowCount, rightDistRowCount);
      } else {
        distRowCount =
            NumberUtil.multiply(leftDistRowCount, rightDistRowCount);
      }
    }

    return RelMdUtil.numDistinctVals(distRowCount, mq.getRowCount(joinRel));
  }
TODO: This method is a copy of {@link RelMdUtil#getJoinDistinctRowCount}.
We will remove it once we replace Math.max with a null-safe method in
Calcite.

Computes the number of distinct rows for a set of keys returned from a
join. Also known as NDV (number of distinct values).

@param joinRel   RelNode representing the join
@param joinType  type of join
@param groupKey  keys that the distinct row count will be computed for
@param predicate join predicate
@param useMaxNdv If true use formula <code>max(left NDV, right NDV)</code>,
                 otherwise use <code>left NDV * right NDV</code>.
@return number of distinct rows
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/stats/HiveRelMdDistinctRowCount.java