@Override
    public RexNode visitCall(RexCall call) {
      if (COMPARISON.contains(call.getOperator().getKind())) {
        RexInputRef ref = null;
        RexLiteral literal = null;
        SqlKind kind = null;
        if (call.operands.get(0) instanceof RexInputRef
            && call.operands.get(1) instanceof RexLiteral) {
          ref = (RexInputRef) call.operands.get(0);
          literal = (RexLiteral) call.operands.get(1);
          kind = call.getOperator().getKind();
        } else if (call.operands.get(1) instanceof RexInputRef
            && call.operands.get(0) instanceof RexLiteral) {
          ref = (RexInputRef) call.operands.get(1);
          literal = (RexLiteral) call.operands.get(0);
          kind = call.getOperator().getKind().reverse();
        }

        // Found an expression that we can try to reduce
        Number max = null;
        Number min = null;
        if (ref != null && literal != null && kind != null) {
          Pair<Number,Number> maxMin = extractMaxMin(ref);
          max = maxMin.left;
          min = maxMin.right;
        }

        if (max != null && min != null) {
          // Stats were available, try to reduce
          RexNode reduced = reduceCall(literal, kind, max, min);
          if (reduced != null) {
            return reduced;
          }
        }

        // We cannot apply the reduction
        return call;
      } else if (HiveIn.INSTANCE.equals(call.op)) {
        if (call.getOperands().get(0) instanceof RexInputRef) {
          // Ref
          RexInputRef ref = (RexInputRef) call.getOperands().get(0);
          // Found an expression that we can try to reduce
          Number max = null;
          Number min = null;
          if (ref != null) {
            Pair<Number,Number> maxMin = extractMaxMin(ref);
            max = maxMin.left;
            min = maxMin.right;
          }

          if (max != null && min != null) {
            // Stats were available, try to reduce
            List<RexNode> newOperands = Lists.newArrayList();
            newOperands.add(ref);
            for (int i = 1; i < call.getOperands().size(); i++) {
              RexNode operand = call.getOperands().get(i);
              if (operand instanceof RexLiteral) {
                RexLiteral literal = (RexLiteral) operand;
                RexNode reduced = reduceCall(literal, SqlKind.EQUALS, max, min);
                if (reduced != null) {
                  if (reduced.isAlwaysTrue()) {
                    return rexBuilder.makeLiteral(true);
                  }
                } else {
                  newOperands.add(literal);
                }
              } else {
                newOperands.add(operand);
              }
            }
            if (newOperands.size() == 1) {
              return rexBuilder.makeLiteral(false);
            }
            return rexBuilder.makeCall(HiveIn.INSTANCE, newOperands);
          }
        } else if (call.getOperands().get(0).getKind() == SqlKind.ROW) {
          // Struct
          RexCall struct = (RexCall) call.getOperands().get(0);
          List<RexInputRef> refs = Lists.newArrayList();
          List<Pair<Number,Number>> maxMinStats = Lists.newArrayList();
          for (RexNode operand: struct.getOperands()) {
            if (!(operand instanceof RexInputRef)) {
              // Cannot simplify, we bail out
              return call;
            }
            RexInputRef ref = (RexInputRef) operand;
            refs.add(ref);
            maxMinStats.add(extractMaxMin(ref));
          }

          // Try to reduce
          List<RexNode> newOperands = Lists.newArrayList();
          newOperands.add(struct);
          for (int i = 1; i < call.getOperands().size(); i++) {
            RexCall constStruct = (RexCall) call.getOperands().get(i);
            boolean allTrue = true;
            boolean addOperand = true;
            for (int j = 0; j < constStruct.getOperands().size(); j++) {
              RexNode operand = constStruct.getOperands().get(j);
              if (operand instanceof RexLiteral) {
                RexLiteral literal = (RexLiteral) operand;
                RexNode reduced = reduceCall(literal, SqlKind.EQUALS,
                        maxMinStats.get(j).left, maxMinStats.get(j).right);
                if (reduced != null) {
                  if (reduced.isAlwaysFalse()) {
                    allTrue = false;
                    addOperand = false;
                    break;
                  }
                } else {
                  allTrue = false;
                }
              } else {
                allTrue = false;
              }
            }
            if (allTrue) {
              return rexBuilder.makeLiteral(true);
            }
            if (addOperand) {
              newOperands.add(constStruct);
            }
          }
          if (newOperands.size() == 1) {
            return rexBuilder.makeLiteral(false);
          }
          return rexBuilder.makeCall(HiveIn.INSTANCE, newOperands);
        }
        // We cannot apply the reduction
        return call;
      } else if (call.getOperator().getKind() == SqlKind.IS_NULL || call.getOperator().getKind() == SqlKind.IS_NOT_NULL) {
        SqlKind kind = call.getOperator().getKind();

        if (call.operands.get(0) instanceof RexInputRef) {
          RexInputRef ref = (RexInputRef) call.operands.get(0);

          ColStatistics stat = extractColStats(ref);
          Long rowCount = extractRowCount(ref);
          if (stat != null && rowCount != null) {
            if (stat.getNumNulls() == 0 || stat.getNumNulls() == rowCount) {
              boolean allNulls = (stat.getNumNulls() == rowCount);

              if (kind == SqlKind.IS_NULL) {
                return rexBuilder.makeLiteral(allNulls);
              } else {
                return rexBuilder.makeLiteral(!allNulls);
              }
            }
          }
        }
      }

      // If we did not reduce, check the children nodes
      RexNode node = super.visitCall(call);
      if (node != call) {
        // FIXME if this rule will make some changes; then it will invoke simplify on all subtrees during exiting the recursion
        RexSimplify simplify =
            new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, filterOp.getCluster().getPlanner().getExecutor());
        node = simplify.simplifyUnknownAs(node,RexUnknownAs.UNKNOWN);

      }
      return node;
    }
Replaces expressions with their reductions. Note that we only have to
look for RexCall, since nothing else is reducible in the first place.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveReduceExpressionsWithStatsRule.java