diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 67057794c64a..368dbe1b6799 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -193,6 +193,7 @@ import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType; +import org.hibernate.type.descriptor.java.StringJavaType; import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType; import org.hibernate.type.descriptor.jdbc.ObjectJdbcType; @@ -5761,7 +5762,10 @@ else if ( ctx.simplePath() != null && ctx.slicedPathAccessFragment() != null ) { final List slicedFragments = ctx.slicedPathAccessFragment().expression(); final SqmTypedNode lhs = (SqmTypedNode) visitSimplePath( ctx.simplePath() ); final SqmExpressible lhsExpressible = lhs.getExpressible(); - if ( lhsExpressible != null && lhsExpressible.getSqmType() instanceof BasicPluralType ) { + if ( lhsExpressible == null ) { + throw new SemanticException( "Slice operator applied to expression of unknown type", query ); + } + else if ( lhsExpressible.getSqmType() instanceof BasicPluralType ) { return getFunctionDescriptor( "array_slice" ).generateSqmExpression( List.of( lhs, @@ -5772,7 +5776,8 @@ else if ( ctx.simplePath() != null && ctx.slicedPathAccessFragment() != null ) { creationContext.getQueryEngine() ); } - else { + else if ( lhsExpressible.getRelationalJavaType() instanceof StringJavaType + && !(lhs instanceof SqmPluralValuedSimplePath) ) { final SqmExpression start = (SqmExpression) slicedFragments.get( 0 ).accept( this ); final SqmExpression end = (SqmExpression) slicedFragments.get( 1 ).accept( this ); return getFunctionDescriptor( "substring" ).generateSqmExpression( @@ -5801,6 +5806,9 @@ else if ( ctx.simplePath() != null && ctx.slicedPathAccessFragment() != null ) { creationContext.getQueryEngine() ); } + else { + throw new SemanticException( "Slice operator applied to expression which is not a string or SQL array", query ); + } } else { throw new ParsingException( "Illegal domain path '" + ctx.getText() + "'" ); 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 f0aa28a40c48..797a97ee9985 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 @@ -2526,6 +2526,23 @@ public void testTupleInSelect(SessionFactoryScope scope) { ); } + @Test + public void testSlice(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + assertEquals("ring", + session.createSelectionQuery("select theString[3:6] from EntityOfBasics", String.class) + .getSingleResult()); + assertEquals('s', + session.createSelectionQuery("select theString[1] from EntityOfBasics", Character.class) + .getSingleResult()); + assertEquals('y', + session.createSelectionQuery("select theString[7] from EntityOfBasics", Character.class) + .getSingleResult()); + } + ); + } + @Test @SkipForDialect(dialectClass = H2Dialect.class) @SkipForDialect(dialectClass = DerbyDialect.class)