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; } }
/** * 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); }
/** * 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(); }
/** * 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; }
/** * Rounds right hand side, if required. Rounding is required when casting from an approximate * numeric to an exact numeric. */ private void roundAsNeeded() { if (SqlTypeUtil.isExactNumeric(lhsType) && SqlTypeUtil.isApproximateNumeric(rhsType)) { rhsExp = roundAway(); } }
/** * 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; }