blob: 6f69f64d187965cce9fa29149a60b248e16c997b [file] [log] [blame]
/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.types;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.hsqldb.OpTypes;
import org.hsqldb.Session;
import org.hsqldb.SessionInterface;
import org.hsqldb.Tokens;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.java.JavaSystem;
import org.hsqldb.store.ValuePool;
/**
* Type subclass for all NUMBER types.<p>
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public final class NumberType extends Type {
static final int tinyintPrecision = 3;
static final int smallintPrecision = 5;
static final int integerPrecision = 10;
static final int bigintPrecision = 19;
static final int doublePrecision = 0;
public static final int defaultNumericPrecision = 100;
public static final int defaultNumericScale = 10;
public static final int maxNumericPrecision = Integer.MAX_VALUE;
static final int bigintSquareNumericPrecision = 40;
//
public static final int TINYINT_WIDTH = 8;
public static final int SMALLINT_WIDTH = 16;
public static final int INTEGER_WIDTH = 32;
public static final int BIGINT_WIDTH = 64;
public static final int DOUBLE_WIDTH = 128; // nominal width
public static final int DECIMAL_WIDTH = 256; // nominal width
//
public static final Type SQL_NUMERIC_DEFAULT_INT =
new NumberType(Types.NUMERIC, defaultNumericPrecision, 0);
//
public static final BigDecimal MAX_LONG =
BigDecimal.valueOf(Long.MAX_VALUE);
public static final BigDecimal MIN_LONG =
BigDecimal.valueOf(Long.MIN_VALUE);
public static final BigDecimal MAX_INT =
BigDecimal.valueOf(Integer.MAX_VALUE);
public static final BigDecimal MIN_INT =
BigDecimal.valueOf(Integer.MIN_VALUE);
//
public static final BigInteger MIN_LONG_BI = MIN_LONG.toBigInteger();
public static final BigInteger MAX_LONG_BI = MAX_LONG.toBigInteger();
//
final int typeWidth;
public NumberType(int type, long precision, int scale) {
super(Types.SQL_NUMERIC, type, precision, scale);
switch (type) {
case Types.TINYINT :
typeWidth = TINYINT_WIDTH;
break;
case Types.SQL_SMALLINT :
typeWidth = SMALLINT_WIDTH;
break;
case Types.SQL_INTEGER :
typeWidth = INTEGER_WIDTH;
break;
case Types.SQL_BIGINT :
typeWidth = BIGINT_WIDTH;
break;
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
typeWidth = DOUBLE_WIDTH;
break;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
typeWidth = DECIMAL_WIDTH;
break;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
/**
* Returns decimal precision for NUMERIC/DECIMAL. Returns binary precision
* for other parts.
*/
public int getPrecision() {
switch (typeCode) {
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
case Types.SQL_BIGINT :
return typeWidth;
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return 64;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return (int) precision;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public int displaySize() {
switch (typeCode) {
case Types.SQL_DECIMAL :
case Types.SQL_NUMERIC :
if (scale == 0) {
if (precision == 0) {
return 646456995; // precision + "-.".length()}
}
return (int) precision + 1;
}
if (precision == scale) {
return (int) precision + 3;
}
return (int) precision + 2;
case Types.SQL_FLOAT :
case Types.SQL_REAL :
case Types.SQL_DOUBLE :
return 23; // String.valueOf(-Double.MAX_VALUE).length();
case Types.SQL_BIGINT :
return 20; // decimal precision + "-".length();
case Types.SQL_INTEGER :
return 11; // decimal precision + "-".length();
case Types.SQL_SMALLINT :
return 6; // decimal precision + "-".length();
case Types.TINYINT :
return 4; // decimal precision + "-".length();
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public int getJDBCTypeCode() {
return typeCode == Types.SQL_BIGINT ? Types.BIGINT
: typeCode;
}
public Class getJDBCClass() {
switch (typeCode) {
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
return java.lang.Integer.class;
case Types.SQL_BIGINT :
return java.lang.Long.class;
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return java.lang.Double.class;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return java.math.BigDecimal.class;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public String getJDBCClassName() {
switch (typeCode) {
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
return "java.lang.Integer";
case Types.SQL_BIGINT :
return "java.lang.Long";
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return "java.lang.Double";
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return "java.math.BigDecimal";
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public String getNameString() {
switch (typeCode) {
case Types.TINYINT :
return Tokens.T_TINYINT;
case Types.SQL_SMALLINT :
return Tokens.T_SMALLINT;
case Types.SQL_INTEGER :
return Tokens.T_INTEGER;
case Types.SQL_BIGINT :
return Tokens.T_BIGINT;
case Types.SQL_REAL :
return Tokens.T_REAL;
case Types.SQL_FLOAT :
return Tokens.T_FLOAT;
case Types.SQL_DOUBLE :
return Tokens.T_DOUBLE;
case Types.SQL_NUMERIC :
return Tokens.T_NUMERIC;
case Types.SQL_DECIMAL :
return Tokens.T_DECIMAL;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public String getFullNameString() {
switch (typeCode) {
case Types.SQL_DOUBLE :
return "DOUBLE PRECISION";
default :
return getNameString();
}
}
public String getDefinition() {
switch (typeCode) {
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
StringBuffer sb = new StringBuffer(16);
sb.append(getNameString());
sb.append('(');
sb.append(precision);
if (scale != 0) {
sb.append(',');
sb.append(scale);
}
sb.append(')');
return sb.toString();
default :
return getNameString();
}
}
public long getMaxPrecision() {
switch (typeCode) {
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return maxNumericPrecision;
default :
return getNumericPrecisionInRadix();
}
}
public int getMaxScale() {
switch (typeCode) {
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return Short.MAX_VALUE;
default :
return 0;
}
}
public boolean acceptsPrecision() {
switch (typeCode) {
case Types.SQL_DECIMAL :
case Types.SQL_NUMERIC :
case Types.SQL_FLOAT :
return true;
default :
return false;
}
}
public boolean acceptsScale() {
switch (typeCode) {
case Types.SQL_DECIMAL :
case Types.SQL_NUMERIC :
return true;
default :
return false;
}
}
public int getPrecisionRadix() {
if (typeCode == Types.SQL_DECIMAL || typeCode == Types.SQL_NUMERIC) {
return 10;
}
return 2;
}
public boolean isNumberType() {
return true;
}
public boolean isIntegralType() {
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return false;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return scale == 0;
default :
return true;
}
}
public boolean isExactNumberType() {
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return false;
default :
return true;
}
}
public int getNominalWidth() {
return typeWidth;
}
public int precedenceDegree(Type other) {
if (other.isNumberType()) {
int otherWidth = ((NumberType) other).typeWidth;
return otherWidth - typeWidth;
}
return Integer.MIN_VALUE;
}
public Type getAggregateType(Type other) {
if (this == other) {
return this;
}
if (other.isCharacterType()) {
return other.getAggregateType(this);
}
switch (other.typeCode) {
case Types.SQL_ALL_TYPES :
return this;
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
case Types.SQL_BIGINT :
break;
default :
throw Error.error(ErrorCode.X_42562);
}
if (typeWidth == DOUBLE_WIDTH) {
return this;
}
if (((NumberType) other).typeWidth == DOUBLE_WIDTH) {
return other;
}
if (typeWidth <= BIGINT_WIDTH
&& ((NumberType) other).typeWidth <= BIGINT_WIDTH) {
return (typeWidth > ((NumberType) other).typeWidth) ? this
: other;
}
int newScale = scale > other.scale ? scale
: other.scale;
long newDigits = precision - scale > other.precision - other.scale
? precision - scale
: other.precision - other.scale;
return getNumberType(Types.SQL_DECIMAL, newDigits + newScale,
newScale);
}
/**
* Returns a SQL type "wide" enough to represent the result of the
* expression.<br>
* A type is "wider" than the other if it can represent all its
* numeric values.<BR>
* Arithmetic operation terms are promoted to a type that can
* represent the resulting values and avoid incorrect results.<p>
* FLOAT/REAL/DOUBLE used in an operation results in the same type,
* regardless of the type of the other operand.
* When the result or the expression is converted to the
* type of the target column for storage, an exception is thrown if the
* resulting value cannot be stored in the column<p>
* Types narrower than INTEGER (int) are promoted to
* INTEGER. The order of promotion is as follows<p>
*
* INTEGER, BIGINT, NUMERIC/DECIMAL<p>
*
* TINYINT and SMALLINT in any combination return INTEGER<br>
* TINYINT/SMALLINT/INTEGER and INTEGER return BIGINT<br>
* TINYINT/SMALLINT/INTEGER and BIGINT return NUMERIC/DECIMAL<br>
* BIGINT and BIGINT return NUMERIC/DECIMAL<br>
* REAL/FLOAT/DOUBLE and any type return REAL/FLOAT/DOUBLE<br>
* NUMERIC/DECIMAL any type other than REAL/FLOAT/DOUBLE returns NUMERIC/DECIMAL<br>
* In the case of NUMERIC/DECIMAL returned, the result precision is always
* large enough to express any value result, while the scale depends on the
* operation:<br>
* For ADD/SUBTRACT/DIVIDE, the scale is the larger of the two<br>
* For MULTIPLY, the scale is the sum of the two scales<br>
*/
public Type getCombinedType(Type other, int operation) {
if (other.typeCode == Types.SQL_ALL_TYPES) {
other = this;
}
switch (operation) {
case OpTypes.ADD :
break;
case OpTypes.MULTIPLY :
if (other.isIntervalType()) {
return other.getCombinedType(this, OpTypes.MULTIPLY);
}
break;
case OpTypes.DIVIDE :
if (typeWidth == DECIMAL_WIDTH) {
break;
}
case OpTypes.SUBTRACT :
default :
// all derivatives of equality ops or comparison ops
return getAggregateType(other);
}
// resolution for ADD and MULTIPLY only
if (!other.isNumberType()) {
throw Error.error(ErrorCode.X_42562);
}
if (typeWidth == DOUBLE_WIDTH
|| ((NumberType) other).typeWidth == DOUBLE_WIDTH) {
return Type.SQL_DOUBLE;
}
int sum = typeWidth + ((NumberType) other).typeWidth;
if (sum <= INTEGER_WIDTH) {
return Type.SQL_INTEGER;
}
if (sum <= BIGINT_WIDTH) {
return Type.SQL_BIGINT;
}
int newScale;
long newDigits;
switch (operation) {
case OpTypes.ADD :
newScale = scale > other.scale ? scale
: other.scale;
newDigits = precision - scale > other.precision - other.scale
? precision - scale
: other.precision - other.scale;
newDigits++;
break;
case OpTypes.DIVIDE :
newDigits = precision - scale + other.scale;
newScale = scale > other.scale ? scale
: other.scale;
break;
case OpTypes.MULTIPLY :
newDigits = precision - scale + other.precision - other.scale;
newScale = scale + other.scale;
break;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
return getNumberType(Types.SQL_DECIMAL, newScale + newDigits,
newScale);
}
public int compare(Session session, Object a, Object b) {
if (a == b) {
return 0;
}
if (a == null) {
return -1;
}
if (b == null) {
return 1;
}
switch (typeCode) {
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER : {
if (b instanceof Integer) {
int ai = ((Number) a).intValue();
int bi = ((Number) b).intValue();
return (ai > bi) ? 1
: (bi > ai ? -1
: 0);
} else if (b instanceof Double) {
double ai = ((Number) a).doubleValue();
double bi = ((Number) b).doubleValue();
return (ai > bi) ? 1
: (bi > ai ? -1
: 0);
} else if (b instanceof BigDecimal) {
BigDecimal ad = convertToDecimal(a);
int i = ad.compareTo((BigDecimal) b);
return (i == 0) ? 0
: (i < 0 ? -1
: 1);
}
}
// fall through
case Types.SQL_BIGINT : {
if (b instanceof Long) {
long longa = ((Number) a).longValue();
long longb = ((Number) b).longValue();
return (longa > longb) ? 1
: (longb > longa ? -1
: 0);
} else if (b instanceof Double) {
BigDecimal ad =
BigDecimal.valueOf(((Number) a).longValue());
BigDecimal bd = new BigDecimal(((Double) b).doubleValue());
int i = ad.compareTo(bd);
return (i == 0) ? 0
: (i < 0 ? -1
: 1);
} else if (b instanceof BigDecimal) {
BigDecimal ad =
BigDecimal.valueOf(((Number) a).longValue());
int i = ad.compareTo((BigDecimal) b);
return (i == 0) ? 0
: (i < 0 ? -1
: 1);
}
}
// fall through
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
/** @todo big-decimal etc */
double ad = ((Number) a).doubleValue();
double bd = ((Number) b).doubleValue();
return (ad > bd) ? 1
: (bd > ad ? -1
: 0);
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL : {
BigDecimal bd = convertToDecimal(b);
int i = ((BigDecimal) a).compareTo(bd);
return (i == 0) ? 0
: (i < 0 ? -1
: 1);
}
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
/** @todo - review usage to see if range enforcement / java type conversion is necessary */
public Object convertToTypeLimits(SessionInterface session, Object a) {
if (a == null) {
return null;
}
switch (typeCode) {
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
case Types.SQL_BIGINT :
return a;
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return a;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL : {
BigDecimal dec = (BigDecimal) a;
if (scale != dec.scale()) {
dec = dec.setScale(scale, BigDecimal.ROUND_HALF_DOWN);
}
int p = JavaSystem.precision(dec);
if (p > precision) {
throw Error.error(ErrorCode.X_22003);
}
return dec;
}
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public Object convertToType(SessionInterface session, Object a,
Type otherType) {
if (a == null) {
return a;
}
if (otherType.typeCode == typeCode) {
switch (typeCode) {
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
if (otherType.scale == scale
&& otherType.precision <= precision) {
return a;
}
break;
default :
return a;
}
}
if (otherType.isIntervalType()) {
int endType = ((IntervalType) otherType).endIntervalType;
switch (endType) {
case Types.SQL_INTERVAL_YEAR :
case Types.SQL_INTERVAL_MONTH :
case Types.SQL_INTERVAL_DAY :
case Types.SQL_INTERVAL_HOUR :
case Types.SQL_INTERVAL_MINUTE : {
Long value = ValuePool.getLong(
((IntervalType) otherType).convertToLong(a));
return convertToType(session, value, Type.SQL_BIGINT);
}
case Types.SQL_INTERVAL_SECOND : {
long seconds = ((IntervalSecondData) a).units;
long nanos = ((IntervalSecondData) a).nanos;
BigDecimal value =
((DTIType) otherType).getSecondPart(seconds, nanos);
return value;
}
}
}
switch (otherType.typeCode) {
case Types.SQL_CLOB :
a = ((ClobData) a).getSubString(
session, 0L, (int) ((ClobData) a).length(session));
// fall through
case Types.SQL_CHAR :
case Types.SQL_VARCHAR :
case Types.VARCHAR_IGNORECASE : {
a = session.getScanner().convertToNumber((String) a, this);
a = convertToDefaultType(session, a);
return convertToTypeLimits(session, a);
}
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
case Types.SQL_BIGINT :
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
break;
case Types.SQL_BIT :
case Types.SQL_BIT_VARYING :
if (otherType.precision == 1) {
if (((BinaryData) a).getBytes()[0] == 0) {
a = ValuePool.INTEGER_0;
} else {
a = ValuePool.INTEGER_1;
}
break;
}
default :
throw Error.error(ErrorCode.X_42561);
}
switch (this.typeCode) {
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
return convertToInt(a, this.typeCode);
case Types.SQL_BIGINT :
return convertToLong(a);
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return convertToDouble(a);
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
BigDecimal value = convertToDecimal(a);
return convertToTypeLimits(session, value);
default :
throw Error.error(ErrorCode.X_42561);
}
}
public Object convertToTypeJDBC(SessionInterface session, Object a,
Type otherType) {
if (a == null) {
return a;
}
if (otherType.isLobType()) {
throw Error.error(ErrorCode.X_42561);
}
switch (otherType.typeCode) {
case Types.SQL_BOOLEAN :
a = ((Boolean) a).booleanValue() ? ValuePool.INTEGER_1
: ValuePool.INTEGER_0;
otherType = Type.SQL_INTEGER;
}
return convertToType(session, a, otherType);
}
/**
* Relaxes SQL parameter type enforcement for DECIMAL, allowing long values.
*/
public Object convertToDefaultType(SessionInterface session, Object a) {
if (a == null) {
return a;
}
Type otherType;
if (a instanceof Number) {
if (a instanceof BigInteger) {
a = new BigDecimal((BigInteger) a);
} else if (a instanceof Float) {
a = new Double(((Float) a).doubleValue());
} else if (a instanceof Byte) {
a = ValuePool.getInt(((Byte) a).intValue());
} else if (a instanceof Short) {
a = ValuePool.getInt(((Short) a).intValue());
}
if (a instanceof Integer) {
otherType = Type.SQL_INTEGER;
} else if (a instanceof Long) {
otherType = Type.SQL_BIGINT;
} else if (a instanceof Double) {
otherType = Type.SQL_DOUBLE;
} else if (a instanceof BigDecimal) {
if (typeCode == Types.SQL_DECIMAL
|| typeCode == Types.SQL_NUMERIC) {
return a;
}
otherType = Type.SQL_DECIMAL_DEFAULT;
} else {
throw Error.error(ErrorCode.X_42561);
}
} else if (a instanceof String) {
otherType = Type.SQL_VARCHAR;
} else {
throw Error.error(ErrorCode.X_42561);
}
return convertToType(session, a, otherType);
}
/**
* Type narrowing from DOUBLE/DECIMAL/NUMERIC to BIGINT / INT / SMALLINT / TINYINT
* following SQL rules. When conversion is from a non-integral type,
* digits to the right of the decimal point are lost.
*/
/**
* Converter from a numeric object to Integer. Input is checked to be
* within range represented by the given number type.
*/
static Integer convertToInt(Object a, int type) {
int value;
if (a instanceof Integer) {
if (type == Types.SQL_INTEGER) {
return (Integer) a;
}
value = ((Integer) a).intValue();
} else if (a instanceof Long) {
long temp = ((Long) a).longValue();
if (Integer.MAX_VALUE < temp || temp < Integer.MIN_VALUE) {
throw Error.error(ErrorCode.X_22003);
}
value = (int) temp;
} else if (a instanceof BigDecimal) {
BigDecimal bd = ((BigDecimal) a);
if (bd.compareTo(MAX_INT) > 0 || bd.compareTo(MIN_INT) < 0) {
throw Error.error(ErrorCode.X_22003);
}
value = bd.intValue();
} else if (a instanceof Double || a instanceof Float) {
double d = ((Number) a).doubleValue();
if (Double.isInfinite(d) || Double.isNaN(d)
|| d >= (double) Integer.MAX_VALUE + 1
|| d <= (double) Integer.MIN_VALUE - 1) {
throw Error.error(ErrorCode.X_22003);
}
value = (int) d;
} else {
throw Error.error(ErrorCode.X_42561);
}
if (type == Types.TINYINT) {
if (Byte.MAX_VALUE < value || value < Byte.MIN_VALUE) {
throw Error.error(ErrorCode.X_22003);
}
} else if (type == Types.SQL_SMALLINT) {
if (Short.MAX_VALUE < value || value < Short.MIN_VALUE) {
throw Error.error(ErrorCode.X_22003);
}
}
return ValuePool.getInt(value);
}
/**
* Converter from a numeric object to Long. Input is checked to be
* within range represented by Long.
*/
static Long convertToLong(Object a) {
if (a instanceof Integer) {
return ValuePool.getLong(((Integer) a).intValue());
} else if (a instanceof Long) {
return (Long) a;
} else if (a instanceof BigDecimal) {
BigDecimal bd = (BigDecimal) a;
if (bd.compareTo(MAX_LONG) > 0 || bd.compareTo(MIN_LONG) < 0) {
throw Error.error(ErrorCode.X_22003);
}
return ValuePool.getLong(bd.longValue());
} else if (a instanceof Double || a instanceof Float) {
double d = ((Number) a).doubleValue();
if (Double.isInfinite(d) || Double.isNaN(d)
|| d >= (double) Long.MAX_VALUE + 1
|| d <= (double) Long.MIN_VALUE - 1) {
throw Error.error(ErrorCode.X_22003);
}
return ValuePool.getLong((long) d);
} else {
throw Error.error(ErrorCode.X_42561);
}
}
/**
* Converter from a numeric object to Double. Input is checked to be
* within range represented by Double
*/
private static Double convertToDouble(Object a) {
double value;
if (a instanceof java.lang.Double) {
return (Double) a;
} else if (a instanceof BigDecimal) {
BigDecimal bd = (BigDecimal) a;
value = bd.doubleValue();
int signum = bd.signum();
BigDecimal bdd = new BigDecimal(value + signum);
if (bdd.compareTo(bd) != signum) {
throw Error.error(ErrorCode.X_22003);
}
} else {
value = ((Number) a).doubleValue();
}
return ValuePool.getDouble(Double.doubleToLongBits(value));
}
public static double toDouble(Object a) {
double value;
if (a instanceof java.lang.Double) {
return ((Double) a).doubleValue();
} else if (a instanceof BigDecimal) {
BigDecimal bd = (BigDecimal) a;
value = bd.doubleValue();
int signum = bd.signum();
BigDecimal bdd = new BigDecimal(value + signum);
if (bdd.compareTo(bd) != signum) {
throw Error.error(ErrorCode.X_22003);
}
} else if (a instanceof Number) {
value = ((Number) a).doubleValue();
} else {
throw Error.error(ErrorCode.X_22501);
}
return value;
}
private static BigDecimal convertToDecimal(Object a) {
if (a instanceof BigDecimal) {
return (BigDecimal) a;
} else if (a instanceof Integer || a instanceof Long) {
return BigDecimal.valueOf(((Number) a).longValue());
} else if (a instanceof Double) {
double value = ((Number) a).doubleValue();
if (Double.isInfinite(value) || Double.isNaN(value)) {
return null;
}
return new BigDecimal(value);
} else {
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public String convertToString(Object a) {
if (a == null) {
return null;
}
switch (this.typeCode) {
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
case Types.SQL_BIGINT :
return a.toString();
case Types.SQL_REAL :
case Types.SQL_DOUBLE :
double value = ((Double) a).doubleValue();
/** @todo - java 5 format change */
if (value == Double.NEGATIVE_INFINITY) {
return "-1E0/0";
}
if (value == Double.POSITIVE_INFINITY) {
return "1E0/0";
}
if (Double.isNaN(value)) {
return "0E0/0E0";
}
String s = Double.toString(value);
// ensure the engine treats the value as a DOUBLE, not DECIMAL
if (s.indexOf('E') < 0) {
s = s.concat("E0");
}
return s;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return JavaSystem.toString((BigDecimal) a);
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public String convertToSQLString(Object a) {
if (a == null) {
return Tokens.T_NULL;
}
return convertToString(a);
}
public boolean canConvertFrom(Type otherType) {
if (otherType.typeCode == Types.SQL_ALL_TYPES) {
return true;
}
if (otherType.isNumberType()) {
return true;
}
if (otherType.isIntervalType()) {
return true;
}
if (otherType.isCharacterType()) {
return true;
}
if (otherType.isBitType() && otherType.precision == 1) {
return true;
}
return false;
}
public int compareToTypeRange(Object o) {
if (!(o instanceof Number)) {
return 0;
}
if (o instanceof Integer || o instanceof Long) {
long temp = ((Number) o).longValue();
int min;
int max;
switch (typeCode) {
case Types.TINYINT :
min = Byte.MIN_VALUE;
max = Byte.MAX_VALUE;
break;
case Types.SQL_SMALLINT :
min = Short.MIN_VALUE;
max = Short.MAX_VALUE;
break;
case Types.SQL_INTEGER :
min = Integer.MIN_VALUE;
max = Integer.MAX_VALUE;
break;
case Types.SQL_BIGINT :
return 0;
case Types.SQL_DECIMAL :
case Types.SQL_NUMERIC : {
if (precision - scale > 18) {
return 0;
}
if (precision - scale > 9 && o instanceof Integer) {
return 0;
}
BigDecimal dec = convertToDecimal(o);
int s = dec.scale();
int p = JavaSystem.precision(dec);
if (s < 0) {
p -= s;
s = 0;
}
return (precision - scale >= p - s) ? 0
: dec.signum();
}
default :
return 0;
}
if (max < temp) {
return 1;
}
if (temp < min) {
return -1;
}
return 0;
}
return 0;
}
public Object add(Object a, Object b, Type otherType) {
if (a == null || b == null) {
return null;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double ad = ((Number) a).doubleValue();
double bd = ((Number) b).doubleValue();
return ValuePool.getDouble(Double.doubleToLongBits(ad + bd));
// return new Double(ad + bd);
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL : {
a = convertToDefaultType(null, a);
b = convertToDefaultType(null, b);
BigDecimal abd = (BigDecimal) a;
BigDecimal bbd = (BigDecimal) b;
abd = abd.add(bbd);
return convertToTypeLimits(null, abd);
}
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER : {
int ai = ((Number) a).intValue();
int bi = ((Number) b).intValue();
return ValuePool.getInt(ai + bi);
}
case Types.SQL_BIGINT : {
long longa = ((Number) a).longValue();
long longb = ((Number) b).longValue();
return ValuePool.getLong(longa + longb);
}
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public Object subtract(Object a, Object b, Type otherType) {
if (a == null || b == null) {
return null;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double ad = ((Number) a).doubleValue();
double bd = ((Number) b).doubleValue();
return ValuePool.getDouble(Double.doubleToLongBits(ad - bd));
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL : {
a = convertToDefaultType(null, a);
b = convertToDefaultType(null, b);
BigDecimal abd = (BigDecimal) a;
BigDecimal bbd = (BigDecimal) b;
abd = abd.subtract(bbd);
return convertToTypeLimits(null, abd);
}
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER : {
int ai = ((Number) a).intValue();
int bi = ((Number) b).intValue();
return ValuePool.getInt(ai - bi);
}
case Types.SQL_BIGINT : {
long longa = ((Number) a).longValue();
long longb = ((Number) b).longValue();
return ValuePool.getLong(longa - longb);
}
default :
}
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
public Object multiply(Object a, Object b) {
if (a == null || b == null) {
return null;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double ad = ((Number) a).doubleValue();
double bd = ((Number) b).doubleValue();
return ValuePool.getDouble(Double.doubleToLongBits(ad * bd));
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL : {
if (!(a instanceof BigDecimal)) {
a = convertToDefaultType(null, a);
}
if (!(b instanceof BigDecimal)) {
b = convertToDefaultType(null, b);
}
BigDecimal abd = (BigDecimal) a;
BigDecimal bbd = (BigDecimal) b;
BigDecimal bd = abd.multiply(bbd);
return convertToTypeLimits(null, bd);
}
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER : {
int ai = ((Number) a).intValue();
int bi = ((Number) b).intValue();
return ValuePool.getInt(ai * bi);
}
case Types.SQL_BIGINT : {
long longa = ((Number) a).longValue();
long longb = ((Number) b).longValue();
return ValuePool.getLong(longa * longb);
}
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public Object divide(Object a, Object b) {
if (a == null || b == null) {
return null;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double ad = ((Number) a).doubleValue();
double bd = ((Number) b).doubleValue();
if (bd == 0) {
throw Error.error(ErrorCode.X_22012);
}
return ValuePool.getDouble(Double.doubleToLongBits(ad / bd));
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL : {
if (!(a instanceof BigDecimal)) {
a = convertToDefaultType(null, a);
}
if (!(b instanceof BigDecimal)) {
b = convertToDefaultType(null, b);
}
BigDecimal abd = (BigDecimal) a;
BigDecimal bbd = (BigDecimal) b;
if (bbd.signum() == 0) {
throw Error.error(ErrorCode.X_22012);
}
BigDecimal bd = abd.divide(bbd, scale, BigDecimal.ROUND_DOWN);
return convertToTypeLimits(null, bd);
}
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER : {
int ai = ((Number) a).intValue();
int bi = ((Number) b).intValue();
if (bi == 0) {
throw Error.error(ErrorCode.X_22012);
}
return ValuePool.getInt(ai / bi);
}
case Types.SQL_BIGINT : {
long al = ((Number) a).longValue();
long bl = ((Number) b).longValue();
if (bl == 0) {
throw Error.error(ErrorCode.X_22012);
}
return ValuePool.getLong(al / bl);
}
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public Object modulo(Object a, Object b, Type otherType) {
if (!otherType.isNumberType()) {
throw Error.error(ErrorCode.X_42561);
}
Object temp = divide(a, b);
temp = multiply(temp, b);
temp = convertToDefaultType(null, temp);
temp = subtract(a, temp, this);
return convertToTypeLimits(null, temp);
}
public Object absolute(Object a) {
return isNegative(a) ? negate(a)
: a;
}
public Object negate(Object a) {
if (a == null) {
return null;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double ad = -((Number) a).doubleValue();
return ValuePool.getDouble(Double.doubleToLongBits(ad));
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return ((BigDecimal) a).negate();
case Types.TINYINT : {
int value = ((Number) a).intValue();
if (value == Byte.MIN_VALUE) {
throw Error.error(ErrorCode.X_22003);
}
return ValuePool.getInt(-value);
}
case Types.SQL_SMALLINT : {
int value = ((Number) a).intValue();
if (value == Short.MIN_VALUE) {
throw Error.error(ErrorCode.X_22003);
}
return ValuePool.getInt(-value);
}
case Types.SQL_INTEGER : {
int value = ((Number) a).intValue();
if (value == Integer.MIN_VALUE) {
throw Error.error(ErrorCode.X_22003);
}
return ValuePool.getInt(-value);
}
case Types.SQL_BIGINT : {
long value = ((Number) a).longValue();
if (value == Long.MIN_VALUE) {
throw Error.error(ErrorCode.X_22003);
}
return ValuePool.getLong(-value);
}
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public int getNumericPrecisionInRadix() {
switch (typeCode) {
case Types.TINYINT :
return 8;
case Types.SQL_SMALLINT :
return 16;
case Types.SQL_INTEGER :
return 32;
case Types.SQL_BIGINT :
return 64;
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return 64;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return (int) precision;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public Type getIntegralType() {
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
return SQL_NUMERIC_DEFAULT_INT;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return scale == 0 ? this
: new NumberType(typeCode, precision, 0);
default :
return this;
}
}
public static boolean isZero(Object a) {
if (a instanceof BigDecimal) {
return ((BigDecimal) a).signum() == 0;
} else if (a instanceof Double) {
return ((Double) a).doubleValue() == 0 || ((Double) a).isNaN();
} else {
return ((Number) a).longValue() == 0;
}
}
public boolean isNegative(Object a) {
if (a == null) {
return false;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double ad = ((Number) a).doubleValue();
return ad < 0;
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return ((BigDecimal) a).signum() < 0;
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
return ((Number) a).intValue() < 0;
case Types.SQL_BIGINT :
return ((Number) a).longValue() < 0;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public int compareToZero(Object a) {
if (a == null) {
return 0;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double ad = ((Number) a).doubleValue();
return ad == 0 ? 0
: ad < 0 ? -1
: 1;
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return ((BigDecimal) a).signum();
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER : {
int ai = ((Number) a).intValue();
return ai == 0 ? 0
: ai < 0 ? -1
: 1;
}
case Types.SQL_BIGINT : {
long al = ((Number) a).longValue();
return al == 0 ? 0
: al < 0 ? -1
: 1;
}
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
public static long scaledDecimal(Object a, int scale) {
if (a == null) {
return 0;
}
if (scale == 0) {
return 0;
}
BigDecimal value = ((BigDecimal) a);
if (value.scale() == 0) {
return 0;
}
value = value.setScale(0, BigDecimal.ROUND_FLOOR);
value = ((BigDecimal) a).subtract(value);
return value.movePointRight(scale).longValue();
}
public static boolean isInLongLimits(BigDecimal result) {
if (NumberType.MIN_LONG.compareTo(result) > 0
|| NumberType.MAX_LONG.compareTo(result) < 0) {
return false;
}
return true;
}
public static boolean isInLongLimits(BigInteger result) {
if (MAX_LONG_BI.compareTo(result) < 0
|| MIN_LONG_BI.compareTo(result) > 0) {
return false;
}
return true;
}
public Object ceiling(Object a) {
if (a == null) {
return null;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double ad = Math.ceil(((Double) a).doubleValue());
if (Double.isInfinite(ad)) {
throw Error.error(ErrorCode.X_22003);
}
return ValuePool.getDouble(Double.doubleToLongBits(ad));
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL : {
BigDecimal value = ((BigDecimal) a).setScale(0,
BigDecimal.ROUND_CEILING);
return value;
}
default :
return a;
}
}
public Object floor(Object a) {
if (a == null) {
return null;
}
switch (typeCode) {
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE : {
double value = Math.floor(((Double) a).doubleValue());
if (Double.isInfinite(value)) {
throw Error.error(ErrorCode.X_22003);
}
return ValuePool.getDouble(Double.doubleToLongBits(value));
}
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL : {
BigDecimal value = ((BigDecimal) a).setScale(0,
BigDecimal.ROUND_FLOOR);
return value;
}
// fall through
default :
return a;
}
}
public Object truncate(Object a, int s) {
if (a == null) {
return null;
}
if (s >= scale) {
return a;
}
BigDecimal dec = convertToDecimal(a);
dec = dec.setScale(s, BigDecimal.ROUND_DOWN);
dec = dec.setScale(scale, BigDecimal.ROUND_DOWN);
a = convertToDefaultType(null, dec);
return convertToTypeLimits(null, a);
}
public static NumberType getNumberType(int type, long precision,
int scale) {
switch (type) {
case Types.SQL_INTEGER :
return SQL_INTEGER;
case Types.SQL_SMALLINT :
return SQL_SMALLINT;
case Types.SQL_BIGINT :
return SQL_BIGINT;
case Types.TINYINT :
return TINYINT;
case Types.SQL_REAL :
case Types.SQL_DOUBLE :
return SQL_DOUBLE;
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
return new NumberType(type, precision, scale);
default :
throw Error.runtimeError(ErrorCode.U_S0500, "NumberType");
}
}
}