From 4791b41cf581f6c2b2c1314ace27b80b0926207c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 21 May 2024 11:07:16 +0200 Subject: [PATCH] HHH-16531 be more forgiving in handling of integral types in schema validation/update Signed-off-by: Gavin King --- .../java/org/hibernate/dialect/Dialect.java | 31 +++++++++++++++++-- .../internal/AbstractSchemaValidator.java | 3 +- .../schema/internal/ColumnDefinitions.java | 4 +-- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index fe93d260e1..bb1ab31b0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -1591,6 +1591,10 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun * {@link Types#VARBINARY BINARY} and * {@link Types#LONGVARBINARY LONGVARBINARY} as the same type, since * Hibernate doesn't really differentiate these types. + *

+ * On the other hand, integral types are not treated as equivalent, + * instead, {@link #isCompatibleIntegralType(int, int)} is responsible + * for determining if the types are compatible. * * @param typeCode1 the first column type info * @param typeCode2 the second column type info @@ -1600,16 +1604,39 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun public boolean equivalentTypes(int typeCode1, int typeCode2) { return typeCode1==typeCode2 || isNumericOrDecimal(typeCode1) && isNumericOrDecimal(typeCode2) -// || isIntegral(typeCode1) && isIntegral(typeCode2) || isFloatOrRealOrDouble(typeCode1) && isFloatOrRealOrDouble(typeCode2) || isVarcharType(typeCode1) && isVarcharType(typeCode2) || isVarbinaryType(typeCode1) && isVarbinaryType(typeCode2) + || isCompatibleIntegralType(typeCode1, typeCode2) // HHH-17908: Since the runtime can cope with enum on the DDL side, // but varchar on the ORM expectation side, let's treat the types as equivalent - || isEnumType( typeCode1 ) && isVarcharType( typeCode2 ) + || isEnumType(typeCode1) && isVarcharType(typeCode2) || sameColumnType(typeCode1, typeCode2); } + /** + * Tolerate storing {@code short} in {@code INTEGER} or {@code BIGINT} + * or {@code int} in {@code BIGINT} for the purposes of schema validation + * and migration. + */ + private boolean isCompatibleIntegralType(int typeCode1, int typeCode2) { + switch (typeCode1) { + case TINYINT: + return typeCode2 == TINYINT + || typeCode2 == SMALLINT + || typeCode2 == INTEGER + || typeCode2 == BIGINT; + case SMALLINT: + return typeCode2 == SMALLINT + || typeCode2 == INTEGER + || typeCode2 == BIGINT; + case INTEGER: + return typeCode2 == INTEGER + || typeCode2 == BIGINT; + } + return false; + } + private boolean sameColumnType(int typeCode1, int typeCode2) { try { return Objects.equals( columnType(typeCode1), columnType(typeCode2) ); diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java index 8e6a630e4b..990261abe0 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java @@ -33,6 +33,7 @@ import org.hibernate.type.descriptor.JdbcTypeNameMapper; import org.jboss.logging.Logger; import static org.hibernate.boot.model.naming.Identifier.toIdentifier; +import static org.hibernate.tool.schema.internal.ColumnDefinitions.hasMatchingType; /** * Base implementation of {@link SchemaValidator}. @@ -163,7 +164,7 @@ public abstract class AbstractSchemaValidator implements SchemaValidator { Metadata metadata, ExecutionOptions options, Dialect dialect) { - if ( !ColumnDefinitions.hasMatchingType( column, columnInformation, metadata, dialect ) ) { + if ( !hasMatchingType( column, columnInformation, metadata, dialect ) ) { throw new SchemaManagementException( String.format( "Schema-validation: wrong column type encountered in column [%s] in " + diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java index 963cd70340..655abf8e2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java @@ -27,7 +27,7 @@ import static org.hibernate.type.SqlTypes.isStringType; class ColumnDefinitions { static boolean hasMatchingType(Column column, ColumnInformation columnInformation, Metadata metadata, Dialect dialect) { - boolean typesMatch = dialect.equivalentTypes( column.getSqlTypeCode(metadata), columnInformation.getTypeCode() ) + final boolean typesMatch = dialect.equivalentTypes( column.getSqlTypeCode( metadata ), columnInformation.getTypeCode() ) || normalize( stripArgs( column.getSqlType( metadata ) ) ).equals( normalize( columnInformation.getTypeName() ) ); if ( typesMatch ) { return true; @@ -42,7 +42,7 @@ class ColumnDefinitions { columnInformation.getDecimalDigits(), metadata.getDatabase().getTypeConfiguration().getJdbcTypeRegistry() ); - return dialect.equivalentTypes( column.getSqlTypeCode(metadata), jdbcType.getDefaultSqlTypeCode() ); + return dialect.equivalentTypes( column.getSqlTypeCode( metadata ), jdbcType.getDefaultSqlTypeCode() ); } }