diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc b/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc index e011110ed1..3ac0efbf41 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc @@ -1120,6 +1120,7 @@ The following functions deal with SQL array types, which are not supported on ev | `array()` | Creates an array based on the passed arguments | `array_contains()` | Whether an array contains an element | `array_contains_null()` | Whether an array contains a null +| `array_position()` | Determines the position of an element in an array |=== ===== `array()` @@ -1153,6 +1154,19 @@ include::{array-example-dir-hql}/ArrayContainsTest.java[tags=hql-array-contains- ---- ==== +[[hql-array-position-functions]] +===== `array_position()` + +Returns the 1-based position of an element in the array. Returns 0 if the element is not found and `null` if the array is `null`. + +[[hql-array-position-example]] +==== +[source, JAVA, indent=0] +---- +include::{array-example-dir-hql}/ArrayPositionTest.java[tags=hql-array-position-example] +---- +==== + [[hql-user-defined-functions]] ==== Native and user-defined functions diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index c1c7c38dce..13fb142ccd 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -465,6 +465,7 @@ public class CockroachLegacyDialect extends Dialect { functionFactory.arrayAggregate(); functionFactory.arrayContains_operator(); functionFactory.arrayContainsNull_array_position(); + functionFactory.arrayPosition_postgresql(); functionContributions.getFunctionRegistry().register( "trunc", diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java index 6c823708b0..b8383233f5 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java @@ -251,6 +251,7 @@ public class HSQLLegacyDialect extends Dialect { functionFactory.arrayAggregate(); functionFactory.arrayContains_hsql(); functionFactory.arrayContainsNull_hsql(); + functionFactory.arrayPosition_hsql(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index 939b6a1dd4..2b855f7ef4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -287,6 +287,7 @@ public class OracleLegacyDialect extends Dialect { functionFactory.arrayAggregate_jsonArrayagg(); functionFactory.arrayContains_oracle(); functionFactory.arrayContainsNull_oracle(); + functionFactory.arrayPosition_oracle(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index 4202f41ffe..a3353a0db0 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -585,6 +585,7 @@ public class PostgreSQLLegacyDialect extends Dialect { functionFactory.arrayAggregate(); functionFactory.arrayContains_operator(); functionFactory.arrayContainsNull_array_position(); + functionFactory.arrayPosition_postgresql(); if ( getVersion().isSameOrAfter( 9, 4 ) ) { functionFactory.makeDateTimeTimestamp(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index a2200444a9..78a6c9cf01 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -452,6 +452,7 @@ public class CockroachDialect extends Dialect { functionFactory.arrayAggregate(); functionFactory.arrayContains_operator(); functionFactory.arrayContainsNull_array_position(); + functionFactory.arrayPosition_postgresql(); functionContributions.getFunctionRegistry().register( "trunc", diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index 79afb5b823..2f6877db07 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -191,6 +191,7 @@ public class HSQLDialect extends Dialect { functionFactory.arrayAggregate(); functionFactory.arrayContains_hsql(); functionFactory.arrayContainsNull_hsql(); + functionFactory.arrayPosition_hsql(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcType.java index 8c493f0b34..0667d0c41c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcType.java @@ -233,19 +233,19 @@ public class OracleArrayJdbcType extends ArrayJdbcType { ); database.addAuxiliaryDatabaseObject( new NamedAuxiliaryDatabaseObject( - arrayTypeName + "_contains", + arrayTypeName + "_position", database.getDefaultNamespace(), new String[]{ - "create or replace function " + arrayTypeName + "_contains(arr in " + arrayTypeName + - ", elem in " + getRawTypeName( elementType ) + ") return number deterministic is begin " + + "create or replace function " + arrayTypeName + "_position(arr in " + arrayTypeName + + ", elem in " + getRawTypeName( elementType ) + ", startPos in number default 1) return number deterministic is begin " + "if arr is null then return null; end if; " + "if elem is null then " + - "for i in 1 .. arr.count loop " + - "if arr(i) is null then return 1; end if; " + + "for i in startPos .. arr.count loop " + + "if arr(i) is null then return i; end if; " + "end loop; " + "else " + - "for i in 1 .. arr.count loop " + - "if arr(i)=elem then return 1; end if; " + + "for i in startPos .. arr.count loop " + + "if arr(i)=elem then return i; end if; " + "end loop; " + "end if; " + "return 0; " + diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 3f24ac6289..d759c7144f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -316,6 +316,7 @@ public class OracleDialect extends Dialect { functionFactory.arrayAggregate_jsonArrayagg(); functionFactory.arrayContains_oracle(); functionFactory.arrayContainsNull_oracle(); + functionFactory.arrayPosition_oracle(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index a84d784a9a..858fb616db 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -633,6 +633,7 @@ public class PostgreSQLDialect extends Dialect { functionFactory.arrayAggregate(); functionFactory.arrayContains_operator(); functionFactory.arrayContainsNull_array_position(); + functionFactory.arrayPosition_postgresql(); functionFactory.makeDateTimeTimestamp(); // Note that PostgreSQL doesn't support the OVER clause for ordered set-aggregate functions diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java index df9b188752..4f742a62d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java @@ -18,6 +18,9 @@ import org.hibernate.dialect.function.array.ArrayAndElementArgumentValidator; import org.hibernate.dialect.function.array.ArrayArgumentValidator; import org.hibernate.dialect.function.array.ArrayConstructorFunction; import org.hibernate.dialect.function.array.ArrayContainsOperatorFunction; +import org.hibernate.dialect.function.array.HSQLArrayPositionFunction; +import org.hibernate.dialect.function.array.OracleArrayPositionFunction; +import org.hibernate.dialect.function.array.PostgreSQLArrayPositionFunction; import org.hibernate.dialect.function.array.CastingArrayConstructorFunction; import org.hibernate.dialect.function.array.OracleArrayAggEmulation; import org.hibernate.dialect.function.array.OracleArrayConstructorFunction; @@ -2691,4 +2694,25 @@ public class CommonFunctionFactory { .setArgumentListSignature( "(ARRAY array)" ) .register(); } + + /** + * CockroachDB and PostgreSQL array_position() function + */ + public void arrayPosition_postgresql() { + functionRegistry.register( "array_position", new PostgreSQLArrayPositionFunction( typeConfiguration ) ); + } + + /** + * HSQL array_position() function + */ + public void arrayPosition_hsql() { + functionRegistry.register( "array_position", new HSQLArrayPositionFunction( typeConfiguration ) ); + } + + /** + * Oracle array_position() function + */ + public void arrayPosition_oracle() { + functionRegistry.register( "array_position", new OracleArrayPositionFunction( typeConfiguration ) ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/AbstractArrayPositionFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/AbstractArrayPositionFunction.java new file mode 100644 index 0000000000..527c05419b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/AbstractArrayPositionFunction.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.dialect.function.array; + +import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; +import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; +import org.hibernate.query.sqm.produce.function.FunctionParameterType; +import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators; +import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * Encapsulates the validator, return type and argument type resolvers for the array_position functions. + * Subclasses only have to implement the rendering. + */ +public abstract class AbstractArrayPositionFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + + public AbstractArrayPositionFunction(TypeConfiguration typeConfiguration) { + super( + "array_position", + new ArgumentTypesValidator( + StandardArgumentsValidators.composite( + StandardArgumentsValidators.between( 2, 3 ), + ArrayAndElementArgumentValidator.DEFAULT_INSTANCE + ), + FunctionParameterType.ANY, + FunctionParameterType.ANY, + FunctionParameterType.INTEGER + ), + StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.standardBasicTypeForJavaType( Integer.class ) ), + (function, argumentIndex, converter) -> { + if ( argumentIndex == 2 ) { + return converter.getCreationContext() + .getSessionFactory() + .getTypeConfiguration() + .standardBasicTypeForJavaType( Integer.class ); + } + else { + return ArrayAndElementArgumentTypeResolver.DEFAULT_INSTANCE.resolveFunctionArgumentType( + function, + argumentIndex, + converter + ); + } + } + ); + } + + @Override + public String getArgumentListSignature() { + return "(ARRAY array, OBJECT element[, INTEGER startPosition])"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayAndElementArgumentValidator.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayAndElementArgumentValidator.java index da2e673fd3..f2adb95d40 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayAndElementArgumentValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayAndElementArgumentValidator.java @@ -37,7 +37,7 @@ public class ArrayAndElementArgumentValidator extends ArrayArgumentValidator { final BasicType expectedElementType = getElementType( arguments, functionName, typeConfiguration ); final SqmTypedNode elementArgument = arguments.get( elementIndex ); final SqmExpressible elementType = elementArgument.getExpressible().getSqmType(); - if ( expectedElementType != elementType ) { + if ( expectedElementType != null && elementType != null && expectedElementType != elementType ) { throw new FunctionArgumentException( String.format( "Parameter %d of function '%s()' has type %s, but argument is of type '%s'", diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayArgumentValidator.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayArgumentValidator.java index 8ab906fe38..3e99d65926 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayArgumentValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayArgumentValidator.java @@ -43,7 +43,10 @@ public class ArrayArgumentValidator implements ArgumentsValidator { TypeConfiguration typeConfiguration) { final SqmTypedNode arrayArgument = arguments.get( arrayIndex ); final SqmExpressible arrayType = arrayArgument.getExpressible().getSqmType(); - if ( !( arrayType instanceof BasicPluralType ) ) { + if ( arrayType == null ) { + return null; + } + else if ( !( arrayType instanceof BasicPluralType ) ) { throw new FunctionArgumentException( String.format( "Parameter %d of function '%s()' requires an array type, but argument is of type '%s'", diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/HSQLArrayPositionFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/HSQLArrayPositionFunction.java new file mode 100644 index 0000000000..e4f86a516e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/HSQLArrayPositionFunction.java @@ -0,0 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * HSQLDB has a special syntax. + */ +public class HSQLArrayPositionFunction extends AbstractArrayPositionFunction { + + public HSQLArrayPositionFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression elementExpression = (Expression) sqlAstArguments.get( 1 ); + sqlAppender.append( "position_array(" ); + elementExpression.accept( walker ); + sqlAppender.append( " in " ); + arrayExpression.accept( walker ); + if ( sqlAstArguments.size() > 2 ) { + sqlAppender.append( " from " ); + sqlAstArguments.get( 2 ).accept( walker ); + } + sqlAppender.append( ')' ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayContainsFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayContainsFunction.java index c944f1cf60..d23000bebf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayContainsFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayContainsFunction.java @@ -39,11 +39,11 @@ public class OracleArrayContainsFunction extends AbstractSqmSelfRenderingFunctio final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); final String arrayTypeName = ArrayTypeHelper.getArrayTypeName( arrayExpression.getExpressionType(), walker ); sqlAppender.appendSql( arrayTypeName ); - sqlAppender.append( "_contains(" ); + sqlAppender.append( "_position(" ); arrayExpression.accept( walker ); sqlAppender.append( ',' ); sqlAstArguments.get( 1 ).accept( walker ); - sqlAppender.append( ")=1" ); + sqlAppender.append( ")>0" ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayContainsNullFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayContainsNullFunction.java index e23ae988af..a8e76c94bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayContainsNullFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayContainsNullFunction.java @@ -39,9 +39,9 @@ public class OracleArrayContainsNullFunction extends AbstractSqmSelfRenderingFun final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); final String arrayTypeName = ArrayTypeHelper.getArrayTypeName( arrayExpression.getExpressionType(), walker ); sqlAppender.appendSql( arrayTypeName ); - sqlAppender.append( "_contains(" ); + sqlAppender.append( "_position(" ); arrayExpression.accept( walker ); - sqlAppender.append( ",null)=1" ); + sqlAppender.append( ",null)>0" ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayPositionFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayPositionFunction.java new file mode 100644 index 0000000000..eff684b683 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayPositionFunction.java @@ -0,0 +1,41 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +public class OracleArrayPositionFunction extends AbstractArrayPositionFunction { + + public OracleArrayPositionFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final String arrayTypeName = ArrayTypeHelper.getArrayTypeName( arrayExpression.getExpressionType(), walker ); + sqlAppender.appendSql( arrayTypeName ); + sqlAppender.append( "_position(" ); + arrayExpression.accept( walker ); + sqlAppender.append( ',' ); + sqlAstArguments.get( 1 ).accept( walker ); + if ( sqlAstArguments.size() > 2 ) { + sqlAppender.append( ',' ); + sqlAstArguments.get( 2 ).accept( walker ); + } + sqlAppender.append( ")" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/PostgreSQLArrayPositionFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/PostgreSQLArrayPositionFunction.java new file mode 100644 index 0000000000..3d56e8f71c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/PostgreSQLArrayPositionFunction.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.dialect.function.array; + +import java.util.List; + +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * PostgreSQL variant of the function. + */ +public class PostgreSQLArrayPositionFunction extends AbstractArrayPositionFunction { + + public PostgreSQLArrayPositionFunction(TypeConfiguration typeConfiguration) { + super( typeConfiguration ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression elementExpression = (Expression) sqlAstArguments.get( 1 ); + sqlAppender.append( "case when " ); + arrayExpression.accept( walker ); + sqlAppender.append( " is not null then coalesce(array_position(" ); + arrayExpression.accept( walker ); + sqlAppender.append( ',' ); + elementExpression.accept( walker ); + if ( sqlAstArguments.size() > 2 ) { + sqlAppender.append( ',' ); + sqlAstArguments.get( 2 ).accept( walker ); + } + sqlAppender.append( "),0) end" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java index 0bef7877a1..e39ca894a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java @@ -94,7 +94,7 @@ public class ArgumentTypesValidator implements ArgumentsValidator { JdbcTypeIndicators indicators = typeConfiguration.getCurrentBaseSqlTypeIndicators(); SqmExpressible nodeType = argument.getNodeType(); FunctionParameterType type = count < types.length ? types[count++] : types[types.length - 1]; - if ( nodeType != null ) { + if ( nodeType != null && type != FunctionParameterType.ANY ) { JavaType javaType = nodeType.getRelationalJavaType(); if (javaType != null) { checkArgumentType( functionName, count, argument, indicators, type, javaType ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayPositionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayPositionTest.java new file mode 100644 index 0000000000..8ba0bb5d52 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayPositionTest.java @@ -0,0 +1,90 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.function.array; + +import java.util.List; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.dialect.HSQLDialect; + +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Christian Beikov + */ +@DomainModel(annotatedClasses = EntityWithArrays.class) +@SessionFactory +@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsStructuralArrays.class) +// Make sure this stuff runs on a dedicated connection pool, +// otherwise we might run into ORA-21700: object does not exist or is marked for delete +// because the JDBC connection or database session caches something that should have been invalidated +@ServiceRegistry(settings = @Setting(name = AvailableSettings.CONNECTION_PROVIDER, value = "")) +@SkipForDialect(dialectClass = H2Dialect.class, reason = "H2 does not have an array_position function") +public class ArrayPositionTest { + + @BeforeEach + public void prepareData(SessionFactoryScope scope) { + scope.inTransaction( em -> { + em.persist( new EntityWithArrays( 1L, new String[]{} ) ); + em.persist( new EntityWithArrays( 2L, new String[]{ "abc", null, "def" } ) ); + em.persist( new EntityWithArrays( 3L, null ) ); + } ); + } + + @AfterEach + public void cleanup(SessionFactoryScope scope) { + scope.inTransaction( em -> { + em.createMutationQuery( "delete from EntityWithArrays" ).executeUpdate(); + } ); + } + + @Test + public void testPosition(SessionFactoryScope scope) { + scope.inSession( em -> { + //tag::hql-array-position-example[] + List results = em.createQuery( "from EntityWithArrays e where array_position(e.theArray, 'abc') = 1", EntityWithArrays.class ) + .getResultList(); + //end::hql-array-position-example[] + assertEquals( 1, results.size() ); + assertEquals( 2L, results.get( 0 ).getId() ); + } ); + } + + @Test + public void testPositionZero(SessionFactoryScope scope) { + scope.inSession( em -> { + List results = em.createQuery( "from EntityWithArrays e where array_position(e.theArray, 'xyz') = 0", EntityWithArrays.class ) + .getResultList(); + assertEquals( 2, results.size() ); + } ); + } + + @Test + @SkipForDialect(dialectClass = HSQLDialect.class, reason = "See https://sourceforge.net/p/hsqldb/bugs/1692/") + public void testPositionNull(SessionFactoryScope scope) { + scope.inSession( em -> { + List results = em.createQuery( "from EntityWithArrays e where array_position(e.theArray, null) = 2", EntityWithArrays.class ) + .getResultList(); + assertEquals( 1, results.size() ); + assertEquals( 2L, results.get( 0 ).getId() ); + } ); + } + +}