From 72e092f67fa2763309788ad12308158a2a0ce8dc Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 18 Aug 2023 20:39:02 +0200 Subject: [PATCH] HHH-16989 fix 'cast(string as String(10))', 'cast(string as Binary(10))' on MySQL --- .../community/dialect/MySQLLegacyDialect.java | 54 +++++++-------- .../org/hibernate/dialect/MySQLDialect.java | 64 +++++++++-------- .../internal/CapacityDependentDdlType.java | 18 ++++- .../descriptor/sql/internal/DdlTypeImpl.java | 69 +++++++++++-------- .../orm/test/query/hql/FunctionTests.java | 68 +++++++++++++----- 5 files changed, 164 insertions(+), 109 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java index 81774e3d9c..7bb542ca6a 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java @@ -299,51 +299,45 @@ public class MySQLLegacyDialect extends Dialect { final int maxLobLen = 65_535; final int maxMediumLobLen = 16_777_215; - final CapacityDependentDdlType.Builder varcharBuilder = CapacityDependentDdlType.builder( - VARCHAR, - columnType( CLOB ), - "char", - this - ) - .withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ) - .withTypeCapacity( maxMediumLobLen, "mediumtext" ); + final CapacityDependentDdlType.Builder varcharBuilder = + CapacityDependentDdlType.builder( VARCHAR, + columnType( CLOB ), columnType( CHAR ), castType( CHAR ), this ) + .withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ) + .withTypeCapacity( maxMediumLobLen, "mediumtext" ); if ( getMaxVarcharLength() < maxLobLen ) { varcharBuilder.withTypeCapacity( maxLobLen, "text" ); } ddlTypeRegistry.addDescriptor( varcharBuilder.build() ); - final CapacityDependentDdlType.Builder nvarcharBuilder = CapacityDependentDdlType.builder( - NVARCHAR, - columnType( NCLOB ), - "char", - this - ) - .withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ) - .withTypeCapacity( maxMediumLobLen, "mediumtext" ); + final CapacityDependentDdlType.Builder nvarcharBuilder = + CapacityDependentDdlType.builder( NVARCHAR, + columnType( NCLOB ), columnType( NCHAR ), castType( NCHAR ), this ) + .withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ) + .withTypeCapacity( maxMediumLobLen, "mediumtext" ); if ( getMaxVarcharLength() < maxLobLen ) { nvarcharBuilder.withTypeCapacity( maxLobLen, "text" ); } ddlTypeRegistry.addDescriptor( nvarcharBuilder.build() ); - final CapacityDependentDdlType.Builder varbinaryBuilder = CapacityDependentDdlType.builder( - VARBINARY, - columnType( BLOB ), - "binary", - this - ) - .withTypeCapacity( getMaxVarbinaryLength(), "varbinary($l)" ) - .withTypeCapacity( maxMediumLobLen, "mediumblob" ); + final CapacityDependentDdlType.Builder varbinaryBuilder = + CapacityDependentDdlType.builder( VARBINARY, + columnType( BLOB ), columnType( BINARY ), castType( BINARY ), this ) + .withTypeCapacity( getMaxVarbinaryLength(), "varbinary($l)" ) + .withTypeCapacity( maxMediumLobLen, "mediumblob" ); if ( getMaxVarbinaryLength() < maxLobLen ) { varbinaryBuilder.withTypeCapacity( maxLobLen, "blob" ); } ddlTypeRegistry.addDescriptor( varbinaryBuilder.build() ); - ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARBINARY, columnType( BLOB ), "binary", this ) ); - ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARCHAR, columnType( CLOB ), "char", this ) ); - ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32NVARCHAR, columnType( CLOB ), "char", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARBINARY, + columnType( BLOB ), castType( BINARY ), this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARCHAR, + columnType( CLOB ), castType( CHAR ), this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32NVARCHAR, + columnType( CLOB ), castType( NCHAR ), this ) ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( BLOB, columnType( BLOB ), "binary", this ) + CapacityDependentDdlType.builder( BLOB, columnType( BLOB ), castType( BINARY ), this ) .withTypeCapacity( maxTinyLobLen, "tinyblob" ) .withTypeCapacity( maxMediumLobLen, "mediumblob" ) .withTypeCapacity( maxLobLen, "blob" ) @@ -351,7 +345,7 @@ public class MySQLLegacyDialect extends Dialect { ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( CLOB, columnType( CLOB ), "char", this ) + CapacityDependentDdlType.builder( CLOB, columnType( CLOB ), castType( CHAR ), this ) .withTypeCapacity( maxTinyLobLen, "tinytext" ) .withTypeCapacity( maxMediumLobLen, "mediumtext" ) .withTypeCapacity( maxLobLen, "text" ) @@ -359,7 +353,7 @@ public class MySQLLegacyDialect extends Dialect { ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( NCLOB, columnType( NCLOB ), "char", this ) + CapacityDependentDdlType.builder( NCLOB, columnType( NCLOB ), castType( NCHAR ), this ) .withTypeCapacity( maxTinyLobLen, "tinytext" ) .withTypeCapacity( maxMediumLobLen, "mediumtext" ) .withTypeCapacity( maxLobLen, "text" ) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 3167e73267..a3295a10c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -297,13 +297,14 @@ public class MySQLDialect extends Dialect { //the default scale is 0 (no decimal places) return "decimal($p,$s)"; case CHAR: - case NCHAR: case VARCHAR: - case NVARCHAR: case LONG32VARCHAR: - case LONG32NVARCHAR: //MySQL doesn't let you cast to TEXT/LONGTEXT return "char"; + case NCHAR: + case NVARCHAR: + case LONG32NVARCHAR: + return "char character set utf8"; case BINARY: case VARBINARY: case LONG32VARBINARY: @@ -332,14 +333,11 @@ public class MySQLDialect extends Dialect { final int maxLobLen = 65_535; final int maxMediumLobLen = 16_777_215; - final CapacityDependentDdlType.Builder varcharBuilder = CapacityDependentDdlType.builder( - VARCHAR, - columnType( CLOB ), - "char", - this - ) - .withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ) - .withTypeCapacity( maxMediumLobLen, "mediumtext" ); + final CapacityDependentDdlType.Builder varcharBuilder = + CapacityDependentDdlType.builder( VARCHAR, + columnType( CLOB ), columnType( CHAR ), castType( CHAR ), this ) + .withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ) + .withTypeCapacity( maxMediumLobLen, "mediumtext" ); if ( getMaxVarcharLength() < maxLobLen ) { varcharBuilder.withTypeCapacity( maxLobLen, "text" ); } @@ -347,38 +345,36 @@ public class MySQLDialect extends Dialect { // do not use nchar/nvarchar/ntext because these // types use a deprecated character set on MySQL 8 - final CapacityDependentDdlType.Builder nvarcharBuilder = CapacityDependentDdlType.builder( - NVARCHAR, - columnType( NCLOB ), - "char character set utf8", - this - ) - .withTypeCapacity( getMaxVarcharLength(), "varchar($l) character set utf8" ) - .withTypeCapacity( maxMediumLobLen, "mediumtext character set utf8" ); + final CapacityDependentDdlType.Builder nvarcharBuilder = + CapacityDependentDdlType.builder( NVARCHAR, + columnType( NCLOB ), columnType( NCHAR ), castType( NCHAR ), this ) + .withTypeCapacity( getMaxVarcharLength(), "varchar($l) character set utf8" ) + .withTypeCapacity( maxMediumLobLen, "mediumtext character set utf8" ); if ( getMaxVarcharLength() < maxLobLen ) { nvarcharBuilder.withTypeCapacity( maxLobLen, "text character set utf8" ); } ddlTypeRegistry.addDescriptor( nvarcharBuilder.build() ); - final CapacityDependentDdlType.Builder varbinaryBuilder = CapacityDependentDdlType.builder( - VARBINARY, - columnType( BLOB ), - "binary", - this - ) - .withTypeCapacity( getMaxVarbinaryLength(), "varbinary($l)" ) - .withTypeCapacity( maxMediumLobLen, "mediumblob" ); + final CapacityDependentDdlType.Builder varbinaryBuilder = + CapacityDependentDdlType.builder( VARBINARY, + columnType( BLOB ), columnType( BINARY ), castType( BINARY ), this ) + .withTypeCapacity( getMaxVarbinaryLength(), "varbinary($l)" ) + .withTypeCapacity( maxMediumLobLen, "mediumblob" ); if ( getMaxVarbinaryLength() < maxLobLen ) { varbinaryBuilder.withTypeCapacity( maxLobLen, "blob" ); } ddlTypeRegistry.addDescriptor( varbinaryBuilder.build() ); - ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARBINARY, columnType( BLOB ), "binary", this ) ); - ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARCHAR, columnType( CLOB ), "char", this ) ); - ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32NVARCHAR, columnType( CLOB ), "char", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARBINARY, + columnType( BLOB ), castType( BINARY ), this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARCHAR, + columnType( CLOB ), castType( CHAR ), this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32NVARCHAR, + columnType( CLOB ), castType( CHAR ), this ) ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( BLOB, columnType( BLOB ), "binary", this ) + CapacityDependentDdlType.builder( BLOB, + columnType( BLOB ), castType( BINARY ), this ) .withTypeCapacity( maxTinyLobLen, "tinyblob" ) .withTypeCapacity( maxMediumLobLen, "mediumblob" ) .withTypeCapacity( maxLobLen, "blob" ) @@ -386,7 +382,8 @@ public class MySQLDialect extends Dialect { ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( CLOB, columnType( CLOB ), "char", this ) + CapacityDependentDdlType.builder( CLOB, + columnType( CLOB ), castType( CHAR ), this ) .withTypeCapacity( maxTinyLobLen, "tinytext" ) .withTypeCapacity( maxMediumLobLen, "mediumtext" ) .withTypeCapacity( maxLobLen, "text" ) @@ -394,7 +391,8 @@ public class MySQLDialect extends Dialect { ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( NCLOB, columnType( NCLOB ), "char character set utf8", this ) + CapacityDependentDdlType.builder( NCLOB, + columnType( NCLOB ), castType( NCHAR ), this ) .withTypeCapacity( maxTinyLobLen, "tinytext character set utf8" ) .withTypeCapacity( maxMediumLobLen, "mediumtext character set utf8" ) .withTypeCapacity( maxLobLen, "text character set utf8" ) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/CapacityDependentDdlType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/CapacityDependentDdlType.java index a033a76160..9a6d27c131 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/CapacityDependentDdlType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/CapacityDependentDdlType.java @@ -26,12 +26,14 @@ public class CapacityDependentDdlType extends DdlTypeImpl { builder.sqlTypeCode, builder.typeNamePattern, builder.castTypeNamePattern, + builder.castTypeName, builder.dialect ); builder.typeEntries.sort( Comparator.naturalOrder() ); this.typeEntries = builder.typeEntries.toArray(new TypeEntry[0]); } + @Override @Deprecated public String[] getRawTypeNames() { final String[] rawTypeNames = new String[typeEntries.length + 1]; for ( int i = 0; i < typeEntries.length; i++ ) { @@ -78,15 +80,25 @@ public class CapacityDependentDdlType extends DdlTypeImpl { public static Builder builder( int sqlTypeCode, String typeNamePattern, - String castTypeNamePattern, + String castTypeName, Dialect dialect) { - return new Builder( sqlTypeCode, typeNamePattern, castTypeNamePattern, dialect ); + return new Builder( sqlTypeCode, typeNamePattern, null, castTypeName, dialect ); + } + + public static Builder builder( + int sqlTypeCode, + String typeNamePattern, + String castTypeNamePattern, + String castTypeName, + Dialect dialect) { + return new Builder( sqlTypeCode, typeNamePattern, castTypeNamePattern, castTypeName, dialect ); } public static class Builder { private final int sqlTypeCode; private final String typeNamePattern; private final String castTypeNamePattern; + private final String castTypeName; private final Dialect dialect; private final List typeEntries; @@ -94,10 +106,12 @@ public class CapacityDependentDdlType extends DdlTypeImpl { int sqlTypeCode, String typeNamePattern, String castTypeNamePattern, + String castTypeName, Dialect dialect) { this.sqlTypeCode = sqlTypeCode; this.typeNamePattern = typeNamePattern; this.castTypeNamePattern = castTypeNamePattern; + this.castTypeName = castTypeName; this.dialect = dialect; this.typeEntries = new ArrayList<>(); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/DdlTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/DdlTypeImpl.java index ef81090faf..1795b7cb4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/DdlTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/DdlTypeImpl.java @@ -10,6 +10,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.Size; import org.hibernate.internal.util.StringHelper; import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.java.CharacterJavaType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.sql.DdlType; @@ -24,24 +25,36 @@ public class DdlTypeImpl implements DdlType { private final int sqlTypeCode; private final String typeNamePattern; private final String castTypeNamePattern; + private final String castTypeName; private final boolean castTypeNameIsStatic; final Dialect dialect; public DdlTypeImpl(int sqlTypeCode, String typeNamePattern, Dialect dialect) { - this( sqlTypeCode, typeNamePattern, typeNamePattern, dialect ); + this( sqlTypeCode, typeNamePattern, typeNamePattern, typeNamePattern, dialect ); } public DdlTypeImpl( int sqlTypeCode, String typeNamePattern, - String castTypeNamePattern, + String castTypeName, + Dialect dialect) { + this( sqlTypeCode, typeNamePattern, null, castTypeName, dialect ); + } + + public DdlTypeImpl( + int sqlTypeCode, + String typeNamePattern, + String castTypeNamePattern, //optional, usually null + String castTypeName, Dialect dialect) { this.sqlTypeCode = sqlTypeCode; this.typeNamePattern = typeNamePattern; this.castTypeNamePattern = castTypeNamePattern; - this.castTypeNameIsStatic = !castTypeNamePattern.contains( "$s" ) - && !castTypeNamePattern.contains( "$l" ) - && !castTypeNamePattern.contains( "$p" ); + this.castTypeName = castTypeName; + this.castTypeNameIsStatic = + !castTypeName.contains( "$s" ) + && !castTypeName.contains( "$p" ) + && !castTypeName.contains( "$l" ); this.dialect = dialect; } @@ -58,7 +71,7 @@ public class DdlTypeImpl implements DdlType { final int parenEnd = typeNamePattern.lastIndexOf( ')' ); return parenEnd + 1 == typeNamePattern.length() ? typeNamePattern.substring( 0, paren ) - : ( typeNamePattern.substring( 0, paren ) + typeNamePattern.substring( parenEnd + 1 ) ); + : typeNamePattern.substring( 0, paren ) + typeNamePattern.substring( parenEnd + 1 ); } return typeNamePattern; } @@ -79,38 +92,40 @@ public class DdlTypeImpl implements DdlType { //needed for cast(x as BigInteger(p)) scale = javaType.getDefaultSqlScale( dialect, jdbcType ); } + return castTypeNamePattern == null + ? getTypeName( length, precision, scale ) + : replace( castTypeNamePattern, length, precision, scale ); } - - return getTypeName( length, precision, scale ); } @Override public String getCastTypeName(JdbcType jdbcType, JavaType javaType) { - if ( castTypeNameIsStatic ) { - return castTypeNamePattern; + if ( javaType instanceof CharacterJavaType && jdbcType.isString() ) { + // nasty special case for casting to Character + return getCastTypeName( jdbcType, javaType, 1L, null, null ); } - Long length = null; - Integer precision = null; - Integer scale = null; + else if ( castTypeNameIsStatic ) { + return castTypeName; + } + else { + final Size size = dialect.getSizeStrategy() + .resolveSize( jdbcType, javaType, null, null, defaultLength( jdbcType ) ); + return replace( castTypeName, size.getLength(), size.getPrecision(), size.getScale() ); + } + } + + //TODO: move this to JdbcType?? + private Long defaultLength(JdbcType jdbcType) { switch ( jdbcType.getDdlTypeCode() ) { case SqlTypes.VARCHAR: - length = (long) dialect.getMaxVarcharLength(); - break; + return (long) dialect.getMaxVarcharLength(); case SqlTypes.NVARCHAR: - length = (long) dialect.getMaxNVarcharLength(); - break; + return (long) dialect.getMaxNVarcharLength(); case SqlTypes.VARBINARY: - length = (long) dialect.getMaxVarbinaryLength(); - break; + return (long) dialect.getMaxVarbinaryLength(); + default: + return null; } - final Size size = dialect.getSizeStrategy().resolveSize( - jdbcType, - javaType, - precision, - scale, - length - ); - return replace( castTypeNamePattern, size.getLength(), size.getPrecision(), size.getScale() ); } /** diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index 4cd6ecabe7..1c0655ea76 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -14,6 +14,7 @@ import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.dialect.TiDBDialect; import org.hibernate.testing.TestForIssue; @@ -57,6 +58,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isOneOf; import static org.hibernate.testing.orm.domain.gambit.EntityOfBasics.Gender.FEMALE; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -997,21 +999,6 @@ public class FunctionTests { ); } - @Test - @RequiresDialectFeature( feature = DialectFeatureChecks.SupportsTruncateThroughCast.class) - @SkipForDialect(dialectClass = MySQLDialect.class, matchSubTypes = true, reason = "We need to fix this, see HHH-16989") - public void testCastFunction_withTruncation(SessionFactoryScope scope) { - scope.inTransaction( - session -> { - session.createQuery("select cast(e.theString as String(15)), cast(e.theDouble as String(8)) from EntityOfBasics e", Object[].class) - .list(); - - session.createQuery("select cast('ABCDEF' as Character) from EntityOfBasics", Character.class) - .list(); - } - ); - } - @Test @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support casting to the binary types") public void testCastFunctionBinary(SessionFactoryScope scope) { @@ -1024,15 +1011,62 @@ public class FunctionTests { } @Test - @SkipForDialect(dialectClass = MySQLDialect.class, matchSubTypes = true, reason = "We need to fix this, see HHH-16989") - @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support casting to the binary types") public void testCastFunctionWithLength(SessionFactoryScope scope) { scope.inTransaction( session -> { session.createQuery("select cast(e.theString as String(15)), cast(e.theDouble as String(17)) from EntityOfBasics e", Object[].class) .list(); + assertEquals( 'A', + session.createQuery("select cast('ABCDEF' as Character)", Character.class) + .getSingleResult() ); + assertEquals( "ABC", + session.createQuery("select cast('ABCDEF' as String(3))", String.class) + .getSingleResult() ); + } + ); + } + + @Test + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support casting to binary types") + @SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "PostgreSQL bytea doesn't have a length") + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle cast to raw does not do truncatation") + public void testCastBinaryWithLength(SessionFactoryScope scope) { + scope.inTransaction( + session -> { session.createQuery("select cast(e.theString as Binary(10)) from EntityOfBasics e", byte[].class) .list(); + assertArrayEquals( new byte[]{(byte)0xFF,(byte)0}, + session.createQuery("select cast(X'FF00EE11' as Binary(2))", byte[].class) + .getSingleResult() ); + } + ); + } + + @Test + @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support casting varchar to binary") + @SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "PostgreSQL bytea doesn't have a length") + public void testCastBinaryWithLengthForOracle(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery("select cast(e.theString as Binary(10)) from EntityOfBasics e", byte[].class) + .list(); + assertArrayEquals( new byte[]{(byte)0xFF,(byte)0}, + session.createQuery("select cast(X'FF00' as Binary(2))", byte[].class) + .getSingleResult() ); + } + ); + } + + @Test + @SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "PostgreSQL bytea doesn't have a length") + public void testCastBinaryWithLengthForDerby(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery("select cast(X'22FF00EE11' as Binary(10))", byte[].class) + .list(); + assertArrayEquals( new byte[]{(byte)0xFF,(byte)0}, + session.createQuery("select cast(X'FF00' as Binary(2))", byte[].class) + .getSingleResult() ); } ); }