[#456] Add runtime support for PRECISION, SCALE, and LENGTH attributes

- Adapted DataType.equals() and hashCode() to consider precision,
scale, and length
 - Consider precision, scale, and length when casting fields/values to a
custom DataType
This commit is contained in:
Lukas Eder 2012-12-21 23:32:25 +01:00
parent 8daa2803a9
commit d7587ee4d7
5 changed files with 67 additions and 94 deletions

View File

@ -367,7 +367,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T725,
}
else if ("BIG_INTEGER".equalsIgnoreCase(field.getName())) {
assertEquals(BigInteger.class, field.getType());
assertEquals(SQLDataType.DECIMAL_INTEGER, field.getDataType());
assertEquals(SQLDataType.DECIMAL_INTEGER.getType(), field.getDataType().getType());
assertTrue(field.getDataType().precision() > 0);
assertEquals(0, field.getDataType().scale());
assertEquals(0, field.getDataType().length());
@ -381,14 +381,14 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T725,
&& getDialect() != SQLDialect.SQLSERVER) {
assertEquals(BigDecimal.class, field.getType());
assertEquals(SQLDataType.DECIMAL, field.getDataType());
assertEquals(SQLDataType.DECIMAL.getType(), field.getDataType().getType());
assertTrue(field.getDataType().precision() > 0);
assertEquals(5, field.getDataType().scale());
assertEquals(0, field.getDataType().length());
}
else if ("BIG_DECIMAL".equalsIgnoreCase(field.getName())) {
assertEquals(BigDecimal.class, field.getType());
assertEquals(SQLDataType.NUMERIC, field.getDataType());
assertEquals(SQLDataType.NUMERIC.getType(), field.getDataType().getType());
assertTrue(field.getDataType().precision() > 0);
assertEquals(5, field.getDataType().scale());
assertEquals(0, field.getDataType().length());

View File

@ -129,26 +129,6 @@ public interface DataType<T> extends Serializable {
*/
String getCastTypeName(Configuration configuration);
/**
* Retrieve the dialect-specific type name associated with this data type
* used for casting
* <p>
* This is useful for some dialects that have specialised type names for
* cast expressions. Other dialects require type-length binding when
* casting, (e.g. VARCHAR(20))
*/
String getCastTypeName(Configuration configuration, int length);
/**
* Retrieve the dialect-specific type name associated with this data type
* used for casting
* <p>
* This is useful for some dialects that have specialised type names for
* cast expressions. Other dialects require type-length binding when
* casting, (e.g. DECIMAL(20,5))
*/
String getCastTypeName(Configuration configuration, int precision, int scale);
/**
* Retrieve the underlying {@link SQLDialect}
*/

View File

@ -78,11 +78,6 @@ class ConvertedDataType<T, U> extends DefaultDataType<U> {
return delegate.getCastTypeName(configuration);
}
@Override
public String getCastTypeName(Configuration configuration, int precision, int scale) {
return delegate.getCastTypeName(configuration, precision, scale);
}
@SuppressWarnings("unchecked")
@Override
public U convert(Object object) {

View File

@ -92,6 +92,12 @@ public class DefaultDataType<T> implements DataType<T> {
*/
private static final Pattern NORMALISE_PATTERN = Pattern.compile("\"|\\.|\\s|\\(\\w+(,\\w+)*\\)|(NOT\\s*NULL)?");
/**
* A pattern to be used to replace all precision, scale, and length
* information
*/
private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("\\([^\\)]*\\)");
// -------------------------------------------------------------------------
// Data type caches
// -------------------------------------------------------------------------
@ -168,6 +174,10 @@ public class DefaultDataType<T> implements DataType<T> {
* The type name used for casting to this type
*/
private final String castTypeName;
/**
* The type name used for casting to this type
*/
private final String castTypeBase;
/**
* The type name
@ -220,6 +230,7 @@ public class DefaultDataType<T> implements DataType<T> {
this.type = type;
this.typeName = typeName;
this.castTypeName = castTypeName;
this.castTypeBase = TYPE_NAME_PATTERN.matcher(castTypeName).replaceAll("").trim();
this.arrayType = (Class<T[]>) Array.newInstance(type, 0).getClass();
if (type == Long.class || type == ULong.class) {
@ -270,11 +281,15 @@ public class DefaultDataType<T> implements DataType<T> {
@Override
public final DataType<T> precision(int p) {
if (hasPrecision()) {
if (precision == p) {
return this;
}
else if (hasPrecision()) {
return new DefaultDataType<T>(dialect, sqlDataType, type, typeName, castTypeName, p, scale, length);
}
return this;
else {
return this;
}
}
@Override
@ -289,11 +304,15 @@ public class DefaultDataType<T> implements DataType<T> {
@Override
public final DataType<T> scale(int s) {
if (hasScale()) {
if (scale == s) {
return this;
}
else if (hasScale()) {
return new DefaultDataType<T>(dialect, sqlDataType, type, typeName, castTypeName, precision, s, length);
}
return this;
else {
return this;
}
}
@Override
@ -308,11 +327,15 @@ public class DefaultDataType<T> implements DataType<T> {
@Override
public final DataType<T> length(int l) {
if (hasLength()) {
if (length == l) {
return this;
}
else if (hasLength()) {
return new DefaultDataType<T>(dialect, sqlDataType, type, typeName, castTypeName, precision, scale, l);
}
return this;
else {
return this;
}
}
@Override
@ -322,16 +345,7 @@ public class DefaultDataType<T> implements DataType<T> {
@Override
public final boolean hasLength() {
return sqlDataType == SQLDataType.BINARY
|| sqlDataType == SQLDataType.BIT
|| sqlDataType == SQLDataType.CHAR
|| sqlDataType == SQLDataType.LONGNVARCHAR
|| sqlDataType == SQLDataType.LONGVARBINARY
|| sqlDataType == SQLDataType.LONGVARCHAR
|| sqlDataType == SQLDataType.NCHAR
|| sqlDataType == SQLDataType.NVARCHAR
|| sqlDataType == SQLDataType.VARBINARY
|| sqlDataType == SQLDataType.VARCHAR;
return type == byte[].class || type == String.class;
}
@Override
@ -345,7 +359,10 @@ public class DefaultDataType<T> implements DataType<T> {
// If this is a SQLDataType find the most suited dialect-specific
// data type
if (getDialect() == null) {
DataType<?> dataType = TYPES_BY_SQL_DATATYPE[configuration.getDialect().ordinal()].get(this);
// Be sure to reset length, precision, and scale, as those values
// were not registered in the below cache
DataType<?> dataType = TYPES_BY_SQL_DATATYPE[configuration.getDialect().ordinal()].get(length(0).precision(0).scale(0));
if (dataType != null) {
return (DataType<T>) dataType;
@ -475,41 +492,20 @@ public class DefaultDataType<T> implements DataType<T> {
@Override
public final String getCastTypeName() {
return castTypeName;
}
@Override
public /* final */ String getCastTypeName(Configuration configuration, int length) {
String result = getCastTypeName(configuration);
if (length != 0) {
// Remove existing length information, first
result = result.replaceAll("\\([^\\)]*\\)", "");
result += "(" + length + ")";
if (length != 0 && hasLength()) {
return castTypeBase + "(" + length + ")";
}
return result;
}
@Override
public /* final */ String getCastTypeName(Configuration configuration, int precision, int scale) {
String result = getCastTypeName(configuration);
if (precision != 0) {
// Remove existing precision / scale information, first
result = result.replaceAll("\\([^\\)]*\\)", "");
if (scale != 0) {
result += "(" + precision + ", " + scale + ")";
else if (precision != 0 && hasPrecision()) {
if (scale != 0 && hasScale()) {
return castTypeBase + "(" + precision + ", " + scale + ")";
}
else {
result += "(" + precision + ")";
return castTypeBase + "(" + precision + ")";
}
}
return result;
else {
return castTypeName;
}
}
@Override
@ -696,6 +692,9 @@ public class DefaultDataType<T> implements DataType<T> {
final int prime = 31;
int result = 1;
result = prime * result + ((dialect == null) ? 0 : dialect.hashCode());
result = prime * result + length;
result = prime * result + precision;
result = prime * result + scale;
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((typeName == null) ? 0 : typeName.hashCode());
return result;
@ -712,6 +711,12 @@ public class DefaultDataType<T> implements DataType<T> {
DefaultDataType<?> other = (DefaultDataType<?>) obj;
if (dialect != other.dialect)
return false;
if (length != other.length)
return false;
if (precision != other.precision)
return false;
if (scale != other.scale)
return false;
if (type == null) {
if (other.type != null)
return false;

View File

@ -194,7 +194,8 @@ class Val<T> extends AbstractField<T> implements Param<T> {
* Render the bind variable including a cast, if necessary
*/
private void toSQLCast(RenderContext context) {
DataType<T> type = getDataType(context).getSQLDataType();
DataType<T> dataType = getDataType(context);
DataType<T> type = dataType.getSQLDataType();
SQLDialect dialect = context.getDialect();
// [#822] Some RDBMS need precision / scale information on BigDecimals
@ -209,7 +210,7 @@ class Val<T> extends AbstractField<T> implements Param<T> {
precision = Math.min(precision, 18);
}
toSQLCast(context, getDataType(context), precision, scale);
toSQLCast(context, dataType, 0, precision, scale);
}
// [#1028] Most databases don't know an OTHER type (except H2, HSQLDB).
@ -217,7 +218,7 @@ class Val<T> extends AbstractField<T> implements Param<T> {
// If the bind value is set, it can be used to derive the cast type
if (value != null) {
toSQLCast(context, DefaultDataType.getDataType(dialect, value.getClass()), 0, 0);
toSQLCast(context, DefaultDataType.getDataType(dialect, value.getClass()), 0, 0, 0);
}
// [#632] [#722] Current integration tests show that Ingres and
@ -229,7 +230,7 @@ class Val<T> extends AbstractField<T> implements Param<T> {
// Derby and DB2 must have a type associated with NULL. Use VARCHAR
// as a workaround. That's probably not correct in all cases, though
else {
toSQLCast(context, DefaultDataType.getDataType(dialect, String.class), 0, 0);
toSQLCast(context, DefaultDataType.getDataType(dialect, String.class), 0, 0, 0);
}
}
@ -244,12 +245,12 @@ class Val<T> extends AbstractField<T> implements Param<T> {
// [#1727] VARCHAR types should be cast to their actual lengths in some
// dialects
else if ((type == SQLDataType.VARCHAR || type == SQLDataType.CHAR) && asList(FIREBIRD).contains(dialect)) {
toSQLCast(context, getDataType(context), getValueLength());
toSQLCast(context, dataType, getValueLength(), 0, 0);
}
// In all other cases, the bind variable can be cast normally
else {
toSQLCast(context, getDataType(context), 0, 0);
toSQLCast(context, dataType, dataType.length(), dataType.precision(), dataType.scale());
}
}
@ -275,19 +276,11 @@ class Val<T> extends AbstractField<T> implements Param<T> {
}
}
private void toSQLCast(RenderContext context, DataType<?> type, int length) {
private void toSQLCast(RenderContext context, DataType<?> type, int length, int precision, int scale) {
context.keyword("cast(");
toSQL(context, getValue(), getType());
context.keyword(" as ")
.sql(type.getCastTypeName(context, length))
.sql(")");
}
private void toSQLCast(RenderContext context, DataType<?> type, int precision, int scale) {
context.keyword("cast(");
toSQL(context, getValue(), getType());
context.keyword(" as ")
.sql(type.getCastTypeName(context, precision, scale))
.sql(type.length(length).precision(precision).scale(scale).getCastTypeName(context))
.sql(")");
}