HHH-17246 - Guard against Sybase being configured for truncating trailing zeros

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2024-10-10 21:25:01 +02:00 committed by Christian Beikov
parent ab9eb9a496
commit 532d5460d4
9 changed files with 196 additions and 2 deletions

View File

@ -90,6 +90,7 @@ import org.hibernate.type.descriptor.jdbc.JsonAsStringJdbcType;
import org.hibernate.type.descriptor.jdbc.XmlArrayJdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.XmlArrayJdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.XmlAsStringArrayJdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.XmlAsStringArrayJdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.XmlAsStringJdbcType; import org.hibernate.type.descriptor.jdbc.XmlAsStringJdbcType;
import org.hibernate.type.descriptor.jdbc.UuidAsBinaryJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.DdlType; import org.hibernate.type.descriptor.sql.DdlType;
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl; import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
@ -752,7 +753,7 @@ public class MetadataBuildingProcess {
); );
} }
else { else {
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.UUID, SqlTypes.BINARY ); jdbcTypeRegistry.addDescriptorIfAbsent( UuidAsBinaryJdbcType.INSTANCE );
} }
jdbcTypeRegistry.addDescriptorIfAbsent( JsonAsStringJdbcType.VARCHAR_INSTANCE ); jdbcTypeRegistry.addDescriptorIfAbsent( JsonAsStringJdbcType.VARCHAR_INSTANCE );

View File

@ -82,6 +82,7 @@ import org.hibernate.tool.schema.spi.Exporter;
import org.hibernate.type.JavaObjectType; import org.hibernate.type.JavaObjectType;
import org.hibernate.type.NullType; import org.hibernate.type.NullType;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.OracleUUIDJavaType;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType; import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
@ -1041,6 +1042,8 @@ public class OracleDialect extends Dialect {
) )
); );
typeContributions.contributeJavaType( OracleUUIDJavaType.INSTANCE );
if(getVersion().isSameOrAfter(23)) { if(getVersion().isSameOrAfter(23)) {
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry(); final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
jdbcTypeRegistry.addDescriptor(OracleEnumJdbcType.INSTANCE); jdbcTypeRegistry.addDescriptor(OracleEnumJdbcType.INSTANCE);

View File

@ -64,6 +64,7 @@ import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC; import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE; import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TINYINT; import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.UUID;
import static org.hibernate.type.SqlTypes.VARBINARY; import static org.hibernate.type.SqlTypes.VARBINARY;
public class OracleAggregateSupport extends AggregateSupportImpl { public class OracleAggregateSupport extends AggregateSupportImpl {
@ -209,6 +210,7 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
case BINARY: case BINARY:
case VARBINARY: case VARBINARY:
case LONG32VARBINARY: case LONG32VARBINARY:
case UUID:
return template.replace( return template.replace(
placeholder, placeholder,
jdbcType.getSqlTypeName() + "_from_json(json_query(" + parentPartExpression + columnExpression + "' returning " + jsonTypeName + "))" jdbcType.getSqlTypeName() + "_from_json(json_query(" + parentPartExpression + columnExpression + "' returning " + jsonTypeName + "))"

View File

@ -0,0 +1,27 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.type.descriptor.java;
import java.util.UUID;
/**
* @author Jan Schatteman
*/
public class OracleUUIDJavaType extends UUIDJavaType {
/* This class is related to the changes that were made for HHH-17246 */
public static final OracleUUIDJavaType INSTANCE = new OracleUUIDJavaType();
@Override
public String toString(UUID value) {
return NoDashesStringTransformer.INSTANCE.transform( value );
}
@Override
public UUID fromString(CharSequence string) {
return NoDashesStringTransformer.INSTANCE.parse( string.toString() );
}
}

View File

@ -37,10 +37,12 @@ public class UUIDJavaType extends AbstractClassJavaType<UUID> {
return true; return true;
} }
@Override
public String toString(UUID value) { public String toString(UUID value) {
return ToStringTransformer.INSTANCE.transform( value ); return ToStringTransformer.INSTANCE.transform( value );
} }
@Override
public UUID fromString(CharSequence string) { public UUID fromString(CharSequence string) {
return ToStringTransformer.INSTANCE.parse( string.toString() ); return ToStringTransformer.INSTANCE.parse( string.toString() );
} }

View File

@ -0,0 +1,60 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.type.descriptor.jdbc;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import static org.hibernate.type.SqlTypes.UUID;
/**
* @author Jan Schatteman
*/
public class UuidAsBinaryJdbcType extends BinaryJdbcType {
public static final UuidAsBinaryJdbcType INSTANCE = new UuidAsBinaryJdbcType();
@Override
public int getDdlTypeCode() {
return Types.BINARY;
}
@Override
public int getDefaultSqlTypeCode() {
return UUID;
}
@Override
public <X> ValueExtractor<X> getExtractor( JavaType<X> javaType ) {
return new BasicExtractor<>( javaType, this ) {
@Override
protected X doExtract( ResultSet rs, int paramIndex, WrapperOptions options ) throws SQLException {
final byte[] bytes = rs.getBytes( paramIndex );
return javaType.wrap( bytes == null || bytes.length == 16 ? bytes : Arrays.copyOf( bytes, 16 ), options );
}
@Override
protected X doExtract( CallableStatement statement, int index, WrapperOptions options ) throws SQLException {
final byte[] bytes = statement.getBytes( index );
return javaType.wrap( bytes == null || bytes.length == 16 ? bytes : Arrays.copyOf( bytes, 16 ), options );
}
@Override
protected X doExtract( CallableStatement statement, String name, WrapperOptions options )
throws SQLException {
final byte[] bytes = statement.getBytes( name );
return javaType.wrap( bytes == null || bytes.length == 16 ? bytes : Arrays.copyOf( bytes, 16 ), options );
}
};
}
}

View File

@ -0,0 +1,81 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.id.uuid;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.JdbcType;
import org.hibernate.dialect.SybaseASEDialect;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author Jan Schatteman
*/
@RequiresDialect(value = SybaseASEDialect.class)
@DomainModel(annotatedClasses = { SybaseASEUUIDTest.Book.class })
@SessionFactory
public class SybaseASEUUIDTest {
private static final UUID uuid = UUID.fromString("53886a8a-7082-4879-b430-25cb94415b00");
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final Book book = new Book(uuid, "John Doe");
session.persist( book );
} );
}
@AfterEach
void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> session.createMutationQuery( "delete from Book" ).executeUpdate()
);
}
@Test
@JiraKey( value = "HHH-17246" )
public void testTrailingZeroByteTruncation(SessionFactoryScope scope) {
scope.inSession(
session -> assertEquals( 15, session.createNativeQuery("select id from Book", byte[].class).getSingleResult().length )
);
scope.inTransaction(
session -> {
Book b = session.createQuery( "from Book", Book.class ).getSingleResult();
assertEquals(uuid, b.id);
}
);
}
@Entity(name = "Book")
static class Book {
@Id
// The purpose is to effectively provoke the trailing 0 bytes truncation
@JdbcType( SybaseUuidAsVarbinaryJdbcType.class )
UUID id;
String author;
public Book() {
}
public Book(UUID id, String author) {
this.id = id;
this.author = author;
}
}
}

View File

@ -0,0 +1,18 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.id.uuid;
import org.hibernate.type.descriptor.jdbc.UuidAsBinaryJdbcType;
import java.sql.Types;
/**
* @author Jan Schatteman
*/
public class SybaseUuidAsVarbinaryJdbcType extends UuidAsBinaryJdbcType {
@Override
public int getDdlTypeCode() {
return Types.VARBINARY;
}
}