[#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:
parent
8daa2803a9
commit
d7587ee4d7
@ -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());
|
||||
|
||||
@ -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}
|
||||
*/
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(")");
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user