public Boolean areColumnsUnique(ProjectRelBase rel, BitSet columns, boolean ignoreNulls) {
    // ProjectRel maps a set of rows to a different set;
    // Without knowledge of the mapping function(whether it
    // preserves uniqueness), it is only safe to derive uniqueness
    // info from the child of a project when the mapping is f(a) => a.
    //
    // Also need to map the input column set to the corresponding child
    // references

    List<RexNode> projExprs = rel.getProjects();
    BitSet childColumns = new BitSet();
    for (int bit : BitSets.toIter(columns)) {
      RexNode projExpr = projExprs.get(bit);
      if (projExpr instanceof RexInputRef) {
        childColumns.set(((RexInputRef) projExpr).getIndex());
      } else if (projExpr instanceof RexCall && ignoreNulls) {
        // If the expression is a cast such that the types are the same
        // except for the nullability, then if we're ignoring nulls,
        // it doesn't matter whether the underlying column reference
        // is nullable.  Check that the types are the same by making a
        // nullable copy of both types and then comparing them.
        RexCall call = (RexCall) projExpr;
        if (call.getOperator() != SqlStdOperatorTable.CAST) {
          continue;
        }
        RexNode castOperand = call.getOperands().get(0);
        if (!(castOperand instanceof RexInputRef)) {
          continue;
        }
        RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory();
        RelDataType castType = typeFactory.createTypeWithNullability(projExpr.getType(), true);
        RelDataType origType = typeFactory.createTypeWithNullability(castOperand.getType(), true);
        if (castType.equals(origType)) {
          childColumns.set(((RexInputRef) castOperand).getIndex());
        }
      } else {
        // If the expression will not influence uniqueness of the
        // projection, then skip it.
        continue;
      }
    }

    // If no columns can affect uniqueness, then return unknown
    if (childColumns.cardinality() == 0) {
      return null;
    }

    return RelMetadataQuery.areColumnsUnique(rel.getChild(), childColumns, ignoreNulls);
  }
    /**
     * Implement the cast expression.
     *
     * <p>TODO: check for overflow
     *
     * @return the rhs expression casted as the lhs type
     */
    public Expression implement() {
      // Check for invalid null assignment. Code generated afterwards
      // can assume null will never be assigned to a not null value.
      checkNotNull();

      // Check for an explicit rhs null value. Code generated
      // afterwards need never check for an explicit null.
      if (rhsType.getSqlTypeName() == SqlTypeName.NULL) {
        if (lhsType.isNullable()) {
          return castFromNull();
        } else {
          // NOTE jvs 27-Jan-2005:  this code will never actually
          // be executed do to previous checkNotNull test, but
          // it still has to compile!
          return rhsAsValue();
        }
      }

      // Case when left hand side is a nullable primitive
      if (translator.isNullablePrimitive(lhsType)) {
        if (SqlTypeUtil.isJavaPrimitive(rhsType)
            && (!rhsType.isNullable() || translator.isNullablePrimitive(rhsType))) {
          return castPrimitiveToNullablePrimitive();
        }
        return castToAssignableValue();
      }

      // Case when left hand side is a not nullable primitive
      if (SqlTypeUtil.isJavaPrimitive(lhsType)) {
        return castToNotNullPrimitive();
      }

      // Case when left hand side is a structure
      if (lhsType.isStruct()) {
        assert (rhsType.isStruct());

        // TODO jvs 27-May-2004:  relax this assert and deal with
        // conversions, null checks, etc.
        assert (lhsType.equals(rhsType));

        return getDirectAssignment();
      }

      // Default is to treat non-primitives as AssignableValue
      return castToAssignableValue();
    }
    private void reduceCasts(RexCall outerCast) {
      RexNode[] operands = outerCast.getOperands();
      if (operands.length != 1) {
        return;
      }
      RelDataType outerCastType = outerCast.getType();
      RelDataType operandType = operands[0].getType();
      if (operandType.equals(outerCastType)) {
        removableCasts.add(outerCast);
        return;
      }

      // See if the reduction
      // CAST((CAST x AS type) AS type NOT NULL)
      // -> CAST(x AS type NOT NULL)
      // applies.  TODO jvs 15-Dec-2008:  consider
      // similar cases for precision changes.
      if (!(operands[0] instanceof RexCall)) {
        return;
      }
      RexCall innerCast = (RexCall) operands[0];
      if (innerCast.getOperator() != SqlStdOperatorTable.castFunc) {
        return;
      }
      if (innerCast.getOperands().length != 1) {
        return;
      }
      RelDataTypeFactory typeFactory = preparingStmt.getFarragoTypeFactory();
      RelDataType outerTypeNullable = typeFactory.createTypeWithNullability(outerCastType, true);
      RelDataType innerTypeNullable = typeFactory.createTypeWithNullability(operandType, true);
      if (outerTypeNullable != innerTypeNullable) {
        return;
      }
      if (operandType.isNullable()) {
        removableCasts.add(innerCast);
      }
    }