diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/spi/DdlTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/spi/DdlTypeRegistry.java index 4996977e48d7..c394864b10a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/spi/DdlTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/spi/DdlTypeRegistry.java @@ -90,12 +90,62 @@ private void addSqlType(DdlType ddlType, int sqlTypeCode) { for ( String rawTypeName : ddlType.getRawTypeNames() ) { final Integer previousSqlTypeCode = sqlTypes.put( rawTypeName, sqlTypeCode ); // Prefer the standard code over a custom code for a certain type name - if ( previousSqlTypeCode != null && JdbcTypeNameMapper.isStandardTypeCode( previousSqlTypeCode ) ) { + if ( previousSqlTypeCode != null + && JdbcTypeNameMapper.isStandardTypeCode( previousSqlTypeCode ) + && (!JdbcTypeNameMapper.isStandardTypeCode( sqlTypeCode ) || isBigger( previousSqlTypeCode, sqlTypeCode )) ) { sqlTypes.put( rawTypeName, previousSqlTypeCode ); } } } + /** + * Whether {@code typeCode1} is bigger than {@code typeCode2}. + * For example, a BIGINT is bigger than INTEGER, but if types are "unrelated", + * e.g. VARCHAR and BIGINT, then this method will always return {@code false}. + */ + private static boolean isBigger(int typeCode1, int typeCode2) { + return switch ( typeCode1 ) { + // Integer type hierarchy: TINYINT < SMALLINT < INTEGER < BIGINT + case SqlTypes.BIGINT -> typeCode2 == SqlTypes.INTEGER + || typeCode2 == SqlTypes.SMALLINT + || typeCode2 == SqlTypes.TINYINT; + case SqlTypes.INTEGER -> typeCode2 == SqlTypes.SMALLINT + || typeCode2 == SqlTypes.TINYINT; + case SqlTypes.SMALLINT -> typeCode2 == SqlTypes.TINYINT; + // Floating point hierarchy: REAL/FLOAT < DOUBLE + case SqlTypes.DOUBLE -> typeCode2 == SqlTypes.REAL + || typeCode2 == SqlTypes.FLOAT; + // Character type hierarchy: CHAR < VARCHAR < LONG32VARCHAR < CLOB + case SqlTypes.CLOB -> typeCode2 == SqlTypes.LONG32VARCHAR + || typeCode2 == SqlTypes.VARCHAR + || typeCode2 == SqlTypes.CHAR; + case SqlTypes.LONG32VARCHAR -> typeCode2 == SqlTypes.VARCHAR + || typeCode2 == SqlTypes.CHAR; + case SqlTypes.VARCHAR -> typeCode2 == SqlTypes.CHAR; + // National character type hierarchy: NCHAR < NVARCHAR < LONG32NVARCHAR < NCLOB + case SqlTypes.NCLOB -> typeCode2 == SqlTypes.LONG32NVARCHAR + || typeCode2 == SqlTypes.NVARCHAR + || typeCode2 == SqlTypes.NCHAR; + case SqlTypes.LONG32NVARCHAR -> typeCode2 == SqlTypes.NVARCHAR + || typeCode2 == SqlTypes.NCHAR; + case SqlTypes.NVARCHAR -> typeCode2 == SqlTypes.NCHAR; + // Binary type hierarchy: BINARY < VARBINARY < LONG32VARBINARY < BLOB + case SqlTypes.BLOB -> typeCode2 == SqlTypes.LONG32VARBINARY + || typeCode2 == SqlTypes.VARBINARY + || typeCode2 == SqlTypes.BINARY; + case SqlTypes.LONG32VARBINARY -> typeCode2 == SqlTypes.VARBINARY + || typeCode2 == SqlTypes.BINARY; + case SqlTypes.VARBINARY -> typeCode2 == SqlTypes.BINARY; + // Temporal type hierarchy: DATE/TIME < TIMESTAMP < TIMESTAMP_WITH_TIMEZONE + case SqlTypes.TIMESTAMP_WITH_TIMEZONE -> typeCode2 == SqlTypes.TIMESTAMP + || typeCode2 == SqlTypes.DATE + || typeCode2 == SqlTypes.TIME; + case SqlTypes.TIMESTAMP -> typeCode2 == SqlTypes.DATE + || typeCode2 == SqlTypes.TIME; + default -> false; + }; + } + /** * Returns the {@link SqlTypes} type code for the given DDL raw type name, or * {@code null} if the type code cannot be determined from the registrations. diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/DdlTypeRegistryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/DdlTypeRegistryTest.java new file mode 100644 index 000000000000..e2e6c76fac34 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/DdlTypeRegistryTest.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.type; + +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@RequiresDialect(DB2Dialect.class) +@SessionFactory +public class DdlTypeRegistryTest { + + @Test + @Jira("https://hibernate.atlassian.net/browse/HHH-20259") + public void testKeepBiggest(SessionFactoryScope scope) { + final DdlTypeRegistry ddlTypeRegistry = scope.getSessionFactory().getTypeConfiguration().getDdlTypeRegistry(); + assertThat( ddlTypeRegistry.getSqlTypeCode( "smallint" ) ).isEqualTo( SqlTypes.SMALLINT ); + } + +}