You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by dj...@apache.org on 2005/05/03 01:58:30 UTC
svn commit: r167832 - in
/incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types:
NumberDataValue.java SQLDecimal.java
Author: djd
Date: Mon May 2 16:58:30 2005
New Revision: 167832
URL: http://svn.apache.org/viewcvs?rev=167832&view=rev
Log:
Fix Derby-225 - Handle negative scale values for BigDecimal values introduced in J2SE 5.0.
Modified:
incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/NumberDataValue.java
incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLDecimal.java
Modified: incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/NumberDataValue.java
URL: http://svn.apache.org/viewcvs/incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/NumberDataValue.java?rev=167832&r1=167831&r2=167832&view=diff
==============================================================================
--- incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/NumberDataValue.java (original)
+++ incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/NumberDataValue.java Mon May 2 16:58:30 2005
@@ -196,14 +196,18 @@
public void setValue(Number theValue) throws StandardException;
/**
- Return the precision of this specific DECIMAL value.
+ Return the SQL precision of this specific DECIMAL value.
+ This does not match the return from BigDecimal.precision()
+ added in J2SE 5.0, which represents the precision of the unscaled value.
If the value does not represent a SQL DECIMAL then
the return is undefined.
*/
public int getDecimalValuePrecision();
/**
- Return the scale of this specific DECIMAL value.
+ Return the SQL scale of this specific DECIMAL value.
+ This does not match the return from BigDecimal.scale()
+ since in J2SE 5.0 onwards that can return negative scales.
If the value does not represent a SQL DECIMAL then
the return is undefined.
*/
Modified: incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLDecimal.java
URL: http://svn.apache.org/viewcvs/incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLDecimal.java?rev=167832&r1=167831&r2=167832&view=diff
==============================================================================
--- incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLDecimal.java (original)
+++ incubator/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLDecimal.java Mon May 2 16:58:30 2005
@@ -343,6 +343,7 @@
}
private static final Method toPlainString;
+ private static final Method bdPrecision;
static {
Method m;
try {
@@ -351,6 +352,12 @@
m = null;
}
toPlainString = m;
+ try {
+ m = BigDecimal.class.getMethod("precision", null);
+ } catch (NoSuchMethodException e) {
+ m = null;
+ }
+ bdPrecision = m;
}
public Object getObject()
@@ -425,8 +432,8 @@
/**
* Distill the BigDecimal to a byte array and
* write out: <UL>
- * <LI> scale (int) </LI>
- * <LI> length of byte array </LI>
+ * <LI> scale (zero or positive) as a byte </LI>
+ * <LI> length of byte array as a byte</LI>
* <LI> the byte array </LI> </UL>
*
*/
@@ -441,12 +448,43 @@
if (value != null) {
scale = value.scale();
+
+ // J2SE 5.0 introduced negative scale value for BigDecimals.
+ // In previouse Java releases a negative scale was not allowed
+ // (threw an exception on setScale and the constructor that took
+ // a scale).
+ //
+ // Thus the Derby format for DECIMAL implictly assumed a
+ // positive or zero scale value, and thus now must explicitly
+ // be positive. This is to allow databases created under J2SE 5.0
+ // to continue to be supported under JDK 1.3/JDK 1.4, ie. to continue
+ // the platform independence, independent of OS/cpu and JVM.
+ //
+ // If the scale is negative set the scale to be zero, this results
+ // in an unchanged value with a new scale. A BigDecimal with a
+ // negative scale by definition is a whole number.
+ // e.g. 1000 can be represented by:
+ // a BigDecimal with scale -3 (unscaled value of 1)
+ // or a BigDecimal with scale 0 (unscaled value of 1000)
+
+ if (scale < 0) {
+ scale = 0;
+ value = value.setScale(0);
+ }
+
BigInteger bi = value.unscaledValue();
byteArray = bi.toByteArray();
} else {
scale = rawScale;
byteArray = rawData;
}
+
+ if (SanityManager.DEBUG)
+ {
+ if (scale < 0)
+ SanityManager.THROWASSERT("DECIMAL scale at writeExternal is negative "
+ + scale + " value " + toString());
+ }
out.writeByte(scale);
out.writeByte(byteArray.length);
@@ -1051,11 +1089,9 @@
{
if (isNull())
return this;
-
- // the getWholeDigits() call will ensure via getBigDecimal()
- // that the rawData is translated into the BigDecimal in value.
+
if (desiredPrecision != IGNORE_PRECISION &&
- ((desiredPrecision - desiredScale) < getWholeDigits()))
+ ((desiredPrecision - desiredScale) < SQLDecimal.getWholeDigits(getBigDecimal())))
{
throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
("DECIMAL/NUMERIC("+desiredPrecision+","+desiredScale+")"));
@@ -1065,31 +1101,43 @@
return this;
}
- public int getDecimalValuePrecision()
- {
- return getPrecision(getBigDecimal());
- }
/**
- *
- * @param decimalValue the big decimal
- *
- * @return the precision
- */
- private static int getPrecision(BigDecimal decimalValue)
+ * Return the SQL scale of this value, number of digits after the
+ * decimal point, or zero for a whole number. This does not match the
+ * return from BigDecimal.scale() since in J2SE 5.0 onwards that can return
+ * negative scales.
+ */
+ public int getDecimalValuePrecision()
{
- if ((decimalValue == null) ||
- decimalValue.equals(ZERO))
- {
+ if (isNull())
return 0;
- }
+
+ BigDecimal localValue = getBigDecimal();
- return SQLDecimal.getWholeDigits(decimalValue) + decimalValue.scale();
+ return SQLDecimal.getWholeDigits(localValue) + getDecimalValueScale();
}
+ /**
+ * Return the SQL scale of this value, number of digits after the
+ * decimal point, or zero for a whole number. This does not match the
+ * return from BigDecimal.scale() since in J2SE 5.0 onwards that can return
+ * negative scales.
+ */
public int getDecimalValueScale()
{
- BigDecimal localValue = getBigDecimal();
- return (localValue == null) ? 0 : localValue.scale();
+ if (isNull())
+ return 0;
+
+ if (value == null)
+ return rawScale;
+
+ int scale = value.scale();
+ if (scale >= 0)
+ return scale;
+
+ // BigDecimal scale is negative, so number must have no fractional
+ // part as its value is the unscaled value * 10^-scale
+ return 0;
}
/**
@@ -1125,19 +1173,14 @@
}
}
- private int getWholeDigits()
- {
- return SQLDecimal.getWholeDigits(getBigDecimal());
- }
-
+ /**
+ * Calculate the number of digits to the left of the decimal point
+ * of the passed in value.
+ * @param decimalValue Value to get whole digits from, never null.
+ * @return number of whole digits.
+ */
private static int getWholeDigits(BigDecimal decimalValue)
{
- if ((decimalValue == null) ||
- decimalValue.equals(ZERO))
- {
- return 0;
- }
-
/**
* if ONE > abs(value) then the number of whole digits is 0
*/
@@ -1146,7 +1189,36 @@
{
return 0;
}
-
+
+ if (JVMInfo.JDK_ID >= JVMInfo.J2SE_15)
+ {
+ // use reflection so we can still compile using JDK1.4
+ // if we are prepared to require 1.5 to compile then this can be a
+ // direct call
+ try {
+ // precision is the number of digits in the unscaled value,
+ // subtracting the scale (positive or negative) will give the
+ // number of whole digits.
+ int precision = ((Integer) bdPrecision.invoke(decimalValue,
+ null)).intValue();
+ return precision - decimalValue.scale();
+ } catch (IllegalAccessException e) {
+ // can't happen based on the JDK spec
+ throw new IllegalAccessError("precision");
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ // can't happen
+ throw new IncompatibleClassChangeError("precision");
+ }
+ }
+
+ }
+
String s = decimalValue.toString();
return (decimalValue.scale() == 0) ? s.length() : s.indexOf('.');
}