1 21 22 package org.apache.derby.iapi.types; 23 24 import org.apache.derby.iapi.reference.SQLState; 25 26 import org.apache.derby.iapi.services.io.ArrayInputStream; 27 28 import org.apache.derby.iapi.services.sanity.SanityManager; 29 30 import org.apache.derby.iapi.services.io.StoredFormatIds; 31 import org.apache.derby.iapi.services.io.Storable; 32 33 import org.apache.derby.iapi.error.StandardException; 34 35 import org.apache.derby.iapi.services.cache.ClassSize; 36 import org.apache.derby.iapi.services.info.JVMInfo; 37 38 import java.math.BigDecimal ; 39 import java.math.BigInteger ; 40 import java.lang.Math ; 41 import java.lang.reflect.Method ; 42 import java.lang.reflect.InvocationTargetException ; 43 import java.io.ObjectOutput ; 44 import java.io.ObjectInput ; 45 import java.io.IOException ; 46 47 import java.sql.ResultSet ; 48 import java.sql.PreparedStatement ; 49 import java.sql.SQLException ; 50 import java.sql.Types ; 51 52 69 public final class SQLDecimal extends NumberDataType implements VariableSizeDataValue 70 { 71 private static final BigDecimal ZERO = BigDecimal.valueOf(0L); 72 private static final BigDecimal ONE = BigDecimal.valueOf(1L); 73 static final BigDecimal MAXLONG_PLUS_ONE = BigDecimal.valueOf(Long.MAX_VALUE).add(ONE); 74 static final BigDecimal MINLONG_MINUS_ONE = BigDecimal.valueOf(Long.MIN_VALUE).subtract(ONE); 75 76 77 78 96 private BigDecimal value; 97 98 101 private byte[] rawData; 102 103 106 private int rawScale; 107 108 private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( SQLDecimal.class); 109 private static final int BIG_DECIMAL_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( BigDecimal .class); 110 111 public int estimateMemoryUsage() 112 { 113 int sz = BASE_MEMORY_USAGE; 114 if( null != value) 115 sz += BIG_DECIMAL_MEMORY_USAGE + (value.unscaledValue().bitLength() + 8)/8; 116 if( null != rawData) 117 sz += rawData.length; 118 return sz; 119 } 120 121 122 128 public SQLDecimal() 129 { 130 } 131 132 public SQLDecimal(BigDecimal val) 133 { 134 value = val; 135 } 136 137 public SQLDecimal(BigDecimal val, int nprecision, int scale) 138 throws StandardException 139 { 140 141 value = val; 142 if ((value != null) && (scale >= 0)) 143 { 144 value = value.setScale(scale, 145 BigDecimal.ROUND_DOWN); 146 } 147 } 148 149 public SQLDecimal(String val) 150 { 151 value = new BigDecimal (val); 152 } 153 154 159 160 161 164 public int getInt() throws StandardException 165 { 166 if (isNull()) 167 return 0; 168 169 try { 170 long lv = getLong(); 171 172 if ((lv >= Integer.MIN_VALUE) && (lv <= Integer.MAX_VALUE)) 173 return (int) lv; 174 175 } catch (StandardException se) { 176 } 177 178 throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER"); 179 } 180 181 184 public byte getByte() throws StandardException 185 { 186 if (isNull()) 187 return (byte)0; 188 189 try { 190 long lv = getLong(); 191 192 if ((lv >= Byte.MIN_VALUE) && (lv <= Byte.MAX_VALUE)) 193 return (byte) lv; 194 195 } catch (StandardException se) { 196 } 197 198 throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "TINYINT"); 199 } 200 201 204 public short getShort() throws StandardException 205 { 206 if (isNull()) 207 return (short)0; 208 209 try { 210 long lv = getLong(); 211 212 if ((lv >= Short.MIN_VALUE) && (lv <= Short.MAX_VALUE)) 213 return (short) lv; 214 215 } catch (StandardException se) { 216 } 217 218 throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "SMALLINT"); 219 } 220 221 224 public long getLong() throws StandardException 225 { 226 BigDecimal localValue = getBigDecimal(); 227 if (localValue == null) 228 return (long)0; 229 230 240 if ( (localValue.compareTo(SQLDecimal.MINLONG_MINUS_ONE) == 1) 241 && (localValue.compareTo(SQLDecimal.MAXLONG_PLUS_ONE) == -1)) { 242 243 return localValue.longValue(); 244 } 245 246 throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "BIGINT"); 247 } 248 249 252 public float getFloat() throws StandardException 253 { 254 BigDecimal localValue = getBigDecimal(); 255 if (localValue == null) 256 return (float)0; 257 258 float value = NumberDataType.normalizeREAL(localValue.floatValue()); 261 262 return value; 263 } 264 265 274 public double getDouble() throws StandardException 275 { 276 BigDecimal localValue = getBigDecimal(); 277 if (localValue == null) 278 return (double)0; 279 280 double value = NumberDataType.normalizeDOUBLE(localValue.doubleValue()); 283 return value; 284 } 285 286 private BigDecimal getBigDecimal() 287 { 288 if ((value == null) && (rawData != null)) 289 { 290 value = new BigDecimal (new BigInteger (rawData), rawScale); 291 } 292 293 return value; 294 } 295 296 300 public int typeToBigDecimal() 301 { 302 return java.sql.Types.DECIMAL; 303 } 304 305 public boolean getBoolean() 307 { 308 309 BigDecimal localValue = getBigDecimal(); 310 if (localValue == null) 311 return false; 312 313 return localValue.compareTo(ZERO) != 0; 314 } 315 316 public String getString() 317 { 318 BigDecimal localValue = getBigDecimal(); 319 if (localValue == null) 320 return null; 321 else if (JVMInfo.JDK_ID < JVMInfo.J2SE_15) 322 return localValue.toString(); 323 else 324 { 325 try { 328 return (String ) toPlainString.invoke(localValue, null); 329 } catch (IllegalAccessException e) { 330 throw new IllegalAccessError ("toPlainString"); 332 } catch (InvocationTargetException e) { 333 Throwable t = e.getTargetException(); 334 if (t instanceof RuntimeException ) { 335 throw (RuntimeException ) t; 336 } else if (t instanceof Error ) { 337 throw (Error ) t; 338 } else { 339 throw new IncompatibleClassChangeError ("toPlainString"); 341 } 342 } 343 } 344 } 345 346 private static final Method toPlainString; 347 private static final Method bdPrecision; 348 static { 349 Method m; 350 try { 351 m = BigDecimal .class.getMethod("toPlainString", null); 352 } catch (NoSuchMethodException e) { 353 m = null; 354 } 355 toPlainString = m; 356 try { 357 m = BigDecimal .class.getMethod("precision", null); 358 } catch (NoSuchMethodException e) { 359 m = null; 360 } 361 bdPrecision = m; 362 } 363 364 public Object getObject() 365 { 366 369 return getBigDecimal(); 370 } 371 372 376 void setObject(Object theValue) throws StandardException 377 { 378 setValue((BigDecimal ) theValue); 379 } 380 381 protected void setFrom(DataValueDescriptor theValue) throws StandardException { 382 383 setCoreValue(SQLDecimal.getBigDecimal(theValue)); 384 } 385 386 public int getLength() 387 { 388 return getDecimalValuePrecision(); 389 } 390 391 public String getTypeName() 393 { 394 return TypeId.DECIMAL_NAME; 395 } 396 397 400 401 406 public int getTypeFormatId() 407 { 408 return StoredFormatIds.SQL_DECIMAL_ID; 409 } 410 411 414 415 public boolean isNull() 416 { 417 return (value == null) && (rawData == null); 418 } 419 420 428 public void writeExternal(ObjectOutput out) throws IOException 429 { 430 if (SanityManager.DEBUG) 432 SanityManager.ASSERT(! isNull()); 433 434 int scale; 435 byte[] byteArray; 436 437 if (value != null) { 438 scale = value.scale(); 439 440 458 if (scale < 0) { 459 scale = 0; 460 value = value.setScale(0); 461 } 462 463 BigInteger bi = value.unscaledValue(); 464 byteArray = bi.toByteArray(); 465 } else { 466 scale = rawScale; 467 byteArray = rawData; 468 } 469 470 if (SanityManager.DEBUG) 471 { 472 if (scale < 0) 473 SanityManager.THROWASSERT("DECIMAL scale at writeExternal is negative " 474 + scale + " value " + toString()); 475 } 476 477 out.writeByte(scale); 478 out.writeByte(byteArray.length); 479 out.write(byteArray); 480 } 481 482 489 public void readExternal(ObjectInput in) throws IOException 490 { 491 value = null; 494 495 rawScale = in.readUnsignedByte(); 496 int size = in.readUnsignedByte(); 497 498 506 if ((rawData == null) || size != rawData.length) 507 { 508 rawData = new byte[size]; 509 } 510 in.readFully(rawData); 511 512 } 513 public void readExternalFromArray(ArrayInputStream in) throws IOException 514 { 515 value = null; 518 519 rawScale = in.readUnsignedByte(); 520 int size = in.readUnsignedByte(); 521 522 530 if ((rawData == null) || size != rawData.length) 531 { 532 rawData = new byte[size]; 533 } 534 in.readFully(rawData); 535 } 536 537 541 public void restoreToNull() 542 { 543 value = null; 544 rawData = null; 545 } 546 547 548 549 protected int typeCompare(DataValueDescriptor arg) throws StandardException 550 { 551 BigDecimal otherValue = SQLDecimal.getBigDecimal(arg); 552 return getBigDecimal().compareTo(otherValue); 553 } 554 555 558 559 563 public DataValueDescriptor getClone() 564 { 565 return new SQLDecimal(getBigDecimal()); 566 } 567 568 571 public DataValueDescriptor getNewNull() 572 { 573 return new SQLDecimal(); 574 } 575 576 581 public void setValueFromResultSet(ResultSet resultSet, int colNumber, 582 boolean isNullable) 583 throws SQLException 584 { 585 value = resultSet.getBigDecimal(colNumber); 586 rawData = null; 587 } 588 593 public final void setInto(PreparedStatement ps, int position) throws SQLException { 594 595 if (isNull()) { 596 ps.setNull(position, java.sql.Types.DECIMAL); 597 return; 598 } 599 600 ps.setBigDecimal(position, getBigDecimal()); 601 } 602 603 614 public void setValue(String theValue) throws StandardException 615 { 616 rawData = null; 617 618 if (theValue == null) 619 { 620 value = null; 621 } 622 else 623 { 624 try 625 { 626 theValue = theValue.trim(); 627 value = new BigDecimal (theValue); 628 rawData = null; 629 } catch (NumberFormatException nfe) 630 { 631 throw invalidFormat(); 632 } 633 } 634 } 635 636 641 public void setValue(double theValue) throws StandardException 642 { 643 setCoreValue(NumberDataType.normalizeDOUBLE(theValue)); 644 } 645 646 650 public void setValue(float theValue) 651 throws StandardException 652 { 653 setCoreValue((double)NumberDataType.normalizeREAL(theValue)); 654 } 655 656 660 public void setValue(long theValue) 661 { 662 value = BigDecimal.valueOf(theValue); 663 rawData = null; 664 } 665 666 670 public void setValue(int theValue) 671 { 672 setValue((long)theValue); 673 } 674 675 679 public void setBigDecimal(Number theValue) throws StandardException 680 { 681 setCoreValue((BigDecimal ) theValue); 682 } 683 684 690 public void setValue(Number theValue) throws StandardException 691 { 692 if (SanityManager.ASSERT) 693 { 694 if (theValue != null && 695 !(theValue instanceof java.math.BigDecimal ) && 696 !(theValue instanceof java.lang.Long )) 697 SanityManager.THROWASSERT("SQLDecimal.setValue(Number) passed a " + theValue.getClass()); 698 } 699 700 if (theValue instanceof BigDecimal || theValue == null) 701 setCoreValue((BigDecimal ) theValue); 702 else 703 setValue(theValue.longValue()); 704 } 705 706 710 public void setValue(boolean theValue) 711 { 712 setCoreValue(theValue ? ONE : ZERO); 713 } 714 715 718 719 720 public int typePrecedence() 721 { 722 return TypeId.DECIMAL_PRECEDENCE; 723 } 724 726 private void setCoreValue(BigDecimal theValue) 727 { 728 value = theValue; 729 rawData = null; 730 } 731 732 private void setCoreValue(double theValue) { 733 value = new BigDecimal (Double.toString(theValue)); 734 rawData = null; 735 } 736 737 752 public void normalize( 753 DataTypeDescriptor desiredType, 754 DataValueDescriptor source) 755 throws StandardException 756 { 757 int desiredScale = desiredType.getScale(); 758 int desiredPrecision = desiredType.getPrecision(); 759 760 setFrom(source); 761 setWidth(desiredPrecision, desiredScale, true); 762 } 763 764 765 768 769 770 782 783 public NumberDataValue plus(NumberDataValue addend1, 784 NumberDataValue addend2, 785 NumberDataValue result) 786 throws StandardException 787 { 788 if (result == null) 789 { 790 result = new SQLDecimal(); 791 } 792 793 if (addend1.isNull() || addend2.isNull()) 794 { 795 result.setToNull(); 796 return result; 797 } 798 799 result.setBigDecimal(SQLDecimal.getBigDecimal(addend1).add(SQLDecimal.getBigDecimal(addend2))); 800 return result; 801 } 802 803 815 816 public NumberDataValue minus(NumberDataValue left, 817 NumberDataValue right, 818 NumberDataValue result) 819 throws StandardException 820 { 821 if (result == null) 822 { 823 result = new SQLDecimal(); 824 } 825 826 if (left.isNull() || right.isNull()) 827 { 828 result.setToNull(); 829 return result; 830 } 831 832 result.setBigDecimal(SQLDecimal.getBigDecimal(left).subtract(SQLDecimal.getBigDecimal(right))); 833 return result; 834 } 835 836 848 849 public NumberDataValue times(NumberDataValue left, 850 NumberDataValue right, 851 NumberDataValue result) 852 throws StandardException 853 { 854 if (result == null) 855 { 856 result = new SQLDecimal(); 857 } 858 859 if (left.isNull() || right.isNull()) 860 { 861 result.setToNull(); 862 return result; 863 } 864 865 result.setBigDecimal(SQLDecimal.getBigDecimal(left).multiply(SQLDecimal.getBigDecimal(right))); 866 return result; 867 } 868 869 881 882 public NumberDataValue divide(NumberDataValue dividend, 883 NumberDataValue divisor, 884 NumberDataValue result) 885 throws StandardException 886 { 887 return divide(dividend, divisor, result, -1); 888 } 889 890 904 905 public NumberDataValue divide(NumberDataValue dividend, 906 NumberDataValue divisor, 907 NumberDataValue result, 908 int scale) 909 throws StandardException 910 { 911 if (result == null) 912 { 913 result = new SQLDecimal(); 914 } 915 916 if (dividend.isNull() || divisor.isNull()) 917 { 918 result.setToNull(); 919 return result; 920 } 921 922 BigDecimal divisorBigDecimal = SQLDecimal.getBigDecimal(divisor); 923 924 if (divisorBigDecimal.compareTo(ZERO) == 0) 925 { 926 throw StandardException.newException(SQLState.LANG_DIVIDE_BY_ZERO); 927 } 928 BigDecimal dividendBigDecimal = SQLDecimal.getBigDecimal(dividend); 929 930 937 result.setBigDecimal(dividendBigDecimal.divide( 938 divisorBigDecimal, 939 scale > -1 ? scale : 940 Math.max((dividendBigDecimal.scale() + 941 SQLDecimal.getWholeDigits(divisorBigDecimal) + 942 1), 943 NumberDataValue.MIN_DECIMAL_DIVIDE_SCALE), 944 BigDecimal.ROUND_DOWN)); 945 946 return result; 947 } 948 949 959 960 public NumberDataValue minus(NumberDataValue result) 961 throws StandardException 962 { 963 if (result == null) 964 { 965 result = new SQLDecimal(); 966 } 967 968 if (this.isNull()) 969 { 970 result.setToNull(); 971 return result; 972 } 973 974 result.setBigDecimal(getBigDecimal().negate()); 975 return result; 976 } 977 978 984 985 protected boolean isNegative() 986 { 987 return !isNull() && (getBigDecimal().compareTo(ZERO) == -1); 988 } 989 990 993 public String toString() 994 { 995 if (isNull()) 996 return "NULL"; 997 else 998 return getString(); 999 } 1000 1001 1004 public int hashCode() 1005 { 1006 long longVal; 1007 BigDecimal localValue = getBigDecimal(); 1008 1009 double doubleVal = (localValue != null) ? localValue.doubleValue() : 0; 1010 1011 if (Double.isInfinite(doubleVal)) 1012 { 1013 1019 longVal = localValue.longValue(); 1020 } 1021 else 1022 { 1023 longVal = (long) doubleVal; 1024 if (longVal != doubleVal) 1025 { 1026 longVal = Double.doubleToLongBits(doubleVal); 1027 } 1028 } 1029 1030 return (int) (longVal ^ (longVal >> 32)); 1031 } 1032 1033 1039 1054 public void setWidth(int desiredPrecision, 1055 int desiredScale, 1056 boolean errorOnTrunc) 1057 throws StandardException 1058 { 1059 if (isNull()) 1060 return; 1061 1062 if (desiredPrecision != IGNORE_PRECISION && 1063 ((desiredPrecision - desiredScale) < SQLDecimal.getWholeDigits(getBigDecimal()))) 1064 { 1065 throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, 1066 ("DECIMAL/NUMERIC("+desiredPrecision+","+desiredScale+")")); 1067 } 1068 value = value.setScale(desiredScale, BigDecimal.ROUND_DOWN); 1069 rawData = null; 1070 } 1071 1072 1078 public int getDecimalValuePrecision() 1079 { 1080 if (isNull()) 1081 return 0; 1082 1083 BigDecimal localValue = getBigDecimal(); 1084 1085 return SQLDecimal.getWholeDigits(localValue) + getDecimalValueScale(); 1086 } 1087 1088 1094 public int getDecimalValueScale() 1095 { 1096 if (isNull()) 1097 return 0; 1098 1099 if (value == null) 1100 return rawScale; 1101 1102 int scale = value.scale(); 1103 if (scale >= 0) 1104 return scale; 1105 1106 return 0; 1109 } 1110 1111 1117 public static BigDecimal getBigDecimal(DataValueDescriptor value) throws StandardException 1118 { 1119 if (SanityManager.DEBUG) 1120 { 1121 if (value.isNull()) 1122 SanityManager.THROWASSERT("NULL value passed to SQLDecimal.getBigDecimal"); 1123 } 1124 1125 switch (value.typeToBigDecimal()) 1126 { 1127 case Types.DECIMAL: 1128 return (BigDecimal ) value.getObject(); 1129 case Types.CHAR: 1130 try { 1131 return new BigDecimal (value.getString().trim()); 1132 } catch (NumberFormatException nfe) { 1133 throw StandardException.newException(SQLState.LANG_FORMAT_EXCEPTION, "java.math.BigDecimal"); 1134 } 1135 case Types.BIGINT: 1136 return BigDecimal.valueOf(value.getLong()); 1137 default: 1138 if (SanityManager.DEBUG) 1139 SanityManager.THROWASSERT("invalid return from " + value.getClass() + ".typeToBigDecimal() " + value.typeToBigDecimal()); 1140 return null; 1141 } 1142 } 1143 1144 1150 private static int getWholeDigits(BigDecimal decimalValue) 1151 { 1152 1155 decimalValue = decimalValue.abs(); 1156 if (ONE.compareTo(decimalValue) == 1) 1157 { 1158 return 0; 1159 } 1160 1161 if (JVMInfo.JDK_ID >= JVMInfo.J2SE_15) 1162 { 1163 try { 1167 int precision = ((Integer ) bdPrecision.invoke(decimalValue, 1171 null)).intValue(); 1172 return precision - decimalValue.scale(); 1173 } catch (IllegalAccessException e) { 1174 throw new IllegalAccessError ("precision"); 1176 } catch (InvocationTargetException e) { 1177 Throwable t = e.getTargetException(); 1178 if (t instanceof RuntimeException ) { 1179 throw (RuntimeException ) t; 1180 } else if (t instanceof Error ) { 1181 throw (Error ) t; 1182 } else { 1183 throw new IncompatibleClassChangeError ("precision"); 1185 } 1186 } 1187 1188 } 1189 1190 String s = decimalValue.toString(); 1191 return (decimalValue.scale() == 0) ? s.length() : s.indexOf('.'); 1192 } 1193} 1194 | Popular Tags |