/**
     * Casts the rhs to an {@link AssignableValue} using that interface's standard assignment
     * method. i.e.
     *
     * <pre>
     * [AssignableValueType] lhs;
     * lhs.[assignMethod](rhs);
     * </pre>
     *
     * or perhaps a type-specific cast:
     *
     * <pre>
     * [AssignableValueType] lhs;
     * lhs.[castMethod](rhs, lhs.getPrecision());
     * </pre>
     *
     * <p>Code is also generated to pad and truncate values which need special handling, such as
     * date and time types. Plus good old null handling.
     */
    private Expression castToAssignableValue() {
      ensureLhs();

      if (requiresSpecializedCast() && rhsType.isNullable()) {
        assert (lhsType.isNullable());

        // propagate null value; normally, we can rely on
        // assignFrom to do it for us, but for specialized casts,
        // we can't
        Expression nullTest =
            new MethodCall(rhsExp, NullableValue.NULL_IND_ACCESSOR_NAME, new ExpressionList());
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp, NullableValue.NULL_IND_MUTATOR_NAME, new ExpressionList(nullTest))));
        StatementList ifStmtList = new StatementList();
        addStatement(new IfStatement(not(nullTest), ifStmtList));
        borrowStmtList(ifStmtList);
        try {
          return castToAssignableValueImpl();
        } finally {
          returnStmtList(ifStmtList);
        }
      } else {
        return castToAssignableValueImpl();
      }
    }
 /**
  * Gets the right hand expression as a valid value to be assigned to the left hand side. Usually
  * returns the original rhs. However, if the lhs is of a primitive type, and the rhs is an
  * explicit null, returns a primitive value instead.
  */
 private Expression rhsAsValue() {
   if (SqlTypeUtil.isJavaPrimitive(lhsType) && (rhsType.getSqlTypeName() == SqlTypeName.NULL)) {
     if (lhsType.getSqlTypeName() == SqlTypeName.BOOLEAN) {
       return Literal.constantFalse();
     } else {
       return Literal.constantZero();
     }
   }
   return rhsExp;
 }
 /** Generates code to throw an exception when a NULL value is casted to a NOT NULL type */
 private void checkNotNull() {
   if (!lhsType.isNullable() && rhsType.isNullable()) {
     rhsExp = rhsAsJava();
     addStatement(
         new ExpressionStatement(
             new MethodCall(
                 translator.getRelImplementor().getConnectionVariable(),
                 "checkNotNull",
                 new ExpressionList(Literal.makeLiteral(targetName), rhsExp))));
   }
 }
    /**
     * Implements a cast from any Java primitive to a nullable Java primitive as a simple
     * assignment. i.e.
     *
     * <pre>
     * [NullablePrimitiveType] lhs;
     * lhs.[nullIndicator] = ...;
     * if (! lhs.[nullIndicator]) {
     *     // check overflow ...
     *     // round ...
     *     lhs.[value] = ...;
     * }
     * </pre>
     */
    private Expression castPrimitiveToNullablePrimitive() {
      ensureLhs();
      boolean nullableSource = rhsType.isNullable();
      Expression rhsIsNull;
      if (nullableSource) {
        rhsIsNull = getNullIndicator(rhsExp);
        rhsExp = getValue(rhsType, rhsExp);
      } else {
        rhsIsNull = Literal.constantFalse();
      }

      addStatement(assign(getNullIndicator(lhsExp), rhsIsNull));
      StatementList setValueBlock = new StatementList();
      StatementList oldList = borrowStmtList(setValueBlock);
      try {
        checkOverflow();
        roundAsNeeded();
        addStatement(assign(getValue(lhsType, lhsExp), new CastExpression(getLhsClass(), rhsExp)));
      } finally {
        returnStmtList(oldList);
      }
      if (nullableSource) {
        addStatement(new IfStatement(not(getNullIndicator(lhsExp)), setValueBlock));
      } else {
        addStatementList(setValueBlock);
      }
      return lhsExp;
    }
    /**
     * Checks for overflow when assigning one primitive type to another. Non-primitive types check
     * for overflow during assignment.
     */
    private void checkOverflow() {
      String maxLiteral = null;
      String minLiteral = null;
      if (lhsType == null) {
        return;
      }

      // Assume that equivalent types can be assigned without overflow
      if (lhsType.getSqlTypeName() == rhsType.getSqlTypeName()) {
        return;
      }

      // Approximate numerics have a wider range than exact numerics
      if (SqlTypeUtil.isApproximateNumeric(lhsType) && SqlTypeUtil.isExactNumeric(rhsType)) {
        return;
      }

      // We can skip an error check if the left type is "larger"
      if (SqlTypeUtil.isIntType(lhsType)
          && SqlTypeUtil.isIntType(rhsType)
          && (SqlTypeUtil.maxValue(lhsType) >= SqlTypeUtil.maxValue(rhsType))) {
        return;
      }
      if (SqlTypeUtil.isExactNumeric(lhsType)) {
        String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType);
        minLiteral = numClassName + ".MIN_VALUE";
        maxLiteral = numClassName + ".MAX_VALUE";
      } else if (SqlTypeUtil.isApproximateNumeric(lhsType)) {
        String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType);
        maxLiteral = numClassName + ".MAX_VALUE";
        minLiteral = "-" + maxLiteral;
      }
      if (maxLiteral == null) {
        return;
      }
      Statement ifstmt =
          new IfStatement(
              new BinaryExpression(
                  new BinaryExpression(
                      rhsExp, BinaryExpression.LESS, new Literal(Literal.STRING, minLiteral)),
                  BinaryExpression.LOGICAL_OR,
                  new BinaryExpression(
                      rhsExp, BinaryExpression.GREATER, new Literal(Literal.STRING, maxLiteral))),
              getThrowStmtList());
      addStatement(ifstmt);
    }
 private boolean requiresSpecializedCast() {
   if ((rhsType != null)
       && (SqlTypeUtil.isNumeric(rhsType) || (rhsType.getSqlTypeName() == SqlTypeName.BOOLEAN))
       && SqlTypeUtil.inCharOrBinaryFamilies(lhsType)
       && !SqlTypeUtil.isLob(lhsType)) {
     // Boolean or Numeric to String.
     // sometimes the Integer got slipped by.
     return true;
   } else {
     return false;
   }
 }
  // implement FarragoOJRexImplementor
  public Expression implementFarrago(
      FarragoRexToOJTranslator translator, RexCall call, Expression[] operands) {
    RelDataType lhsType = call.getType();
    RelDataType rhsType = call.operands[0].getType();
    Expression rhsExp = operands[0];

    SqlTypeName lhsTypeName = lhsType.getSqlTypeName();
    if ((lhsTypeName == SqlTypeName.CURSOR) || (lhsTypeName == SqlTypeName.COLUMN_LIST)) {
      // Conversion should already have been taken care of outside.
      return rhsExp;
    }

    // NOTE jvs 19-Nov-2008:  In some cases (e.g. FRG-273) a cast
    // may be illegal at the SQL level, but allowable as part of
    // implementation, so don't try to enforce
    // SqlTypeUtil.canCastFrom here.  Anything which was supposed
    // to have been prevented should already have been caught
    // by the validator.

    CastHelper helper =
        new CastHelper(translator, null, call.toString(), lhsType, rhsType, null, rhsExp);

    return helper.implement();
  }
    /**
     * 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();
    }
    /**
     * Casts the rhs to a non nullable primitive value. Non nullable primitive values only have a
     * single value field.
     */
    private Expression castToNotNullPrimitive() {
      // If the left and the right types are the same, perform a
      // trivial cast.
      if (lhsType == rhsType) {
        return getDirectAssignment();
      }

      // Retrieve the value of the right side if it is a nullable
      // primitive or a Datetime or an Interval type.
      // TODO: is Decimal a nullable primitive?
      if (translator.isNullablePrimitive(rhsType)
          || SqlTypeUtil.isDatetime(rhsType)
          || SqlTypeUtil.isInterval(rhsType)) {
        rhsExp = getValue(rhsType, rhsExp);
      }

      // Get the name of the numeric class such as Byte, Short, etc.
      String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType);
      OJClass lhsClass = getLhsClass();

      // When casting from a string (or binary) to a number, trim the
      // value and perform the cast by calling a class-specific parsing
      // function.
      if ((numClassName != null)
          && SqlTypeUtil.inCharOrBinaryFamilies(rhsType)
          && !SqlTypeUtil.isLob(rhsType)) {
        // TODO: toString will cause too much garbage collection.
        rhsExp = new MethodCall(rhsExp, "toString", new ExpressionList());
        rhsExp = new MethodCall(rhsExp, "trim", new ExpressionList());
        String methodName = "parse" + numClassName;
        if (lhsType.getSqlTypeName() == SqlTypeName.INTEGER) {
          methodName = "parseInt";
        }
        rhsExp =
            new MethodCall(
                new Literal(Literal.STRING, numClassName), methodName, new ExpressionList(rhsExp));

        Variable outTemp = translator.getRelImplementor().newVariable();
        translator.addStatement(
            new VariableDeclaration(
                TypeName.forOJClass(lhsClass), new VariableDeclarator(outTemp.toString(), rhsExp)));
        rhsExp = outTemp;

        // Note: this check for overflow should only be required
        // when the string conversion does not perform a check.
        checkOverflow();
      } else if ((lhsType.getSqlTypeName() == SqlTypeName.BOOLEAN)
          && SqlTypeUtil.inCharOrBinaryFamilies(rhsType)
          && !SqlTypeUtil.isLob(rhsType)) {
        // Casting from string to boolean relies on the runtime type.
        // Note: string is trimmed by conversion method.

        // TODO: toString will cause too much garbage collection.
        Expression str = new MethodCall(rhsExp, "toString", new ExpressionList());

        rhsExp =
            new MethodCall(
                OJClass.forClass(NullablePrimitive.NullableBoolean.class),
                "convertString",
                new ExpressionList(str));
      } else {
        // In general, check for overflow
        checkOverflow();
      }

      roundAsNeeded();

      rhsExp = new CastExpression(lhsClass, rhsExp);
      return getDirectAssignment();
    }
    private Expression castToAssignableValueImpl() {
      if (requiresSpecializedCast()) {
        if (rhsType.isNullable() && (!SqlTypeUtil.isDecimal(rhsType))) {
          rhsExp = getValue(rhsType, rhsExp);
        }
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp,
                    "cast",
                    new ExpressionList(rhsExp, Literal.makeLiteral(lhsType.getPrecision())))));
      } else {
        // Set current_date for casting time to timestamp. If
        // rhsType is null then we may have to be ready for anything.
        // But it will be null even for current_timestamp, so the
        // condition below seems a bit excessive.
        if ((lhsType.getSqlTypeName() == SqlTypeName.TIMESTAMP)
            && ((rhsType == null) || (rhsType.getSqlTypeName() == SqlTypeName.TIME))) {
          addStatement(
              new ExpressionStatement(
                  new MethodCall(lhsExp, "setCurrentDate", new ExpressionList(getCurrentDate()))));
        }
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp, AssignableValue.ASSIGNMENT_METHOD_NAME, new ExpressionList(rhsExp))));
      }

      // Trim precision of datetime values.
      //
      if (((lhsType.getSqlTypeName() == SqlTypeName.TIMESTAMP)
          || (lhsType.getSqlTypeName() == SqlTypeName.TIME))) {
        if ((rhsType != null)
            // FIXME: JavaType(java.sql.Time) and
            // JavaType(java.sql.Timestamp) say they support precision
            // but do not.
            && !rhsType.toString().startsWith("JavaType(")
            && rhsType.getSqlTypeName().allowsPrec()
            && (lhsType.getPrecision() < rhsType.getPrecision())) {
          int lhsPrecision = lhsType.getPrecision();
          if (lhsPrecision == -1) {
            lhsPrecision = 0;
          }
          addStatement(
              new ExpressionStatement(
                  new MethodCall(
                      lhsExp,
                      SqlDateTimeWithoutTZ.ADJUST_PRECISION_METHOD_NAME,
                      new ExpressionList(Literal.makeLiteral(lhsPrecision)))));
        }
      }

      boolean mayNeedPadOrTruncate = false;
      if (SqlTypeUtil.inCharOrBinaryFamilies(lhsType) && !SqlTypeUtil.isLob(lhsType)) {
        mayNeedPadOrTruncate = true;
      }
      if (mayNeedPadOrTruncate) {
        // check overflow if it is datetime.
        // TODO: should check it at the run time.
        // so, it should be in the
        // cast(SqlDateTimeWithTZ, int precision);
        if ((rhsType != null) && (rhsType.getSqlTypeName() != null)) {
          SqlTypeName typeName = rhsType.getSqlTypeName();
          int precision = 0;
          switch (typeName) {
            case DATE:
              precision = 10;
              break;
            case TIME:
              precision = 8;
              break;
            case TIMESTAMP:
              precision = 19;
              break;
          }
          if ((precision != 0) && (precision > lhsType.getPrecision())) {
            addStatement(
                new IfStatement(
                    new BinaryExpression(
                        Literal.makeLiteral(precision),
                        BinaryExpression.GREATER,
                        Literal.makeLiteral(lhsType.getPrecision())),
                    getThrowStmtList()));
          }
        }
        if ((rhsType != null)
            && (rhsType.getFamily() == lhsType.getFamily())
            && !SqlTypeUtil.isLob(rhsType)) {
          // we may be able to skip pad/truncate based on
          // known facts about source and target precisions
          if (SqlTypeUtil.isBoundedVariableWidth(lhsType)) {
            if (lhsType.getPrecision() >= rhsType.getPrecision()) {
              // target precision is greater than source
              // precision, so truncation is impossible
              // and we can skip adjustment
              return lhsExp;
            }
          } else {
            if ((lhsType.getPrecision() == rhsType.getPrecision())
                && !SqlTypeUtil.isBoundedVariableWidth(rhsType)) {
              // source and target are both fixed-width, and
              // precisions are the same, so there's no adjustment
              // needed
              return lhsExp;
            }
          }
        }

        // determine target precision
        Expression precisionExp = Literal.makeLiteral(lhsType.getPrecision());

        // need to pad only for fixed width
        Expression needPadExp = Literal.makeLiteral(!SqlTypeUtil.isBoundedVariableWidth(lhsType));

        // pad character is 0 for binary, space for character
        Expression padByteExp;
        if (!SqlTypeUtil.inCharFamily(lhsType)) {
          padByteExp = new CastExpression(OJSystem.BYTE, Literal.makeLiteral(0));
        } else {
          padByteExp = new CastExpression(OJSystem.BYTE, Literal.makeLiteral(' '));
        }

        // generate the call to do the job
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp,
                    BytePointer.ENFORCE_PRECISION_METHOD_NAME,
                    new ExpressionList(precisionExp, needPadExp, padByteExp))));
      }

      return lhsExp;
    }