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 ed3e297ff3..dd04d86507 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc @@ -1123,6 +1123,11 @@ The following functions deal with SQL array types, which are not supported on ev | `array_position()` | Determines the position of an element in an array | `array_length()` | Determines the length of an array | `array_concat()` | Concatenates array with each other in order +| `array_contains_all()` | Determines if one array holds all elements of another array +| `array_contains_all_nullable()` | Determines if one array holds all elements of another array, supporting null elements +| `array_contains_any()` | Determines if one array holds at least one element of another array +| `array_contains_any_nullable()` | Determines if one array holds at least one element of another array, supporting null elements +| `array_get()` | Accesses the element of an array by index |=== ===== `array()` @@ -1241,6 +1246,20 @@ include::{array-example-dir-hql}/ArrayContainsAnyTest.java[tags=hql-array-contai ---- ==== +[[hql-array-get-functions]] +===== `array_get()` + +Returns the element of an array at the given 1-based index. Returns `null` if either of the arguments is `null`, +and also if the index is bigger than the array length. + +[[hql-array-get-example]] +==== +[source, JAVA, indent=0] +---- +include::{array-example-dir-hql}/ArrayGetTest.java[tags=hql-array-get-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 581fe75159..0b998d2a56 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 @@ -472,6 +472,7 @@ public class CockroachLegacyDialect extends Dialect { functionFactory.arrayContainsAny_operator(); functionFactory.arrayContainsAllNullable_operator(); functionFactory.arrayContainsAnyNullable_operator(); + functionFactory.arrayGet_bracket(); functionContributions.getFunctionRegistry().register( "trunc", diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java index 4938cab6bc..a740d1a4f8 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java @@ -380,6 +380,7 @@ public class H2LegacyDialect extends Dialect { functionFactory.arrayContainsAny_h2(); functionFactory.arrayContainsAllNullable_h2(); functionFactory.arrayContainsAnyNullable_h2(); + functionFactory.arrayGet_h2(); } else { // Use group_concat until 2.x as listagg was buggy 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 717a697e8d..37e96a24a0 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 @@ -258,6 +258,7 @@ public class HSQLLegacyDialect extends Dialect { functionFactory.arrayContainsAny_hsql(); functionFactory.arrayContainsAllNullable_hsql(); functionFactory.arrayContainsAnyNullable_hsql(); + functionFactory.arrayGet_unnest(); } @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 c613f2012c..ead0e7558b 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 @@ -294,6 +294,7 @@ public class OracleLegacyDialect extends Dialect { functionFactory.arrayContainsAny_oracle(); functionFactory.arrayContainsAllNullable_oracle(); functionFactory.arrayContainsAnyNullable_oracle(); + functionFactory.arrayGet_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 f4727319da..aac61a9c6d 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 @@ -592,6 +592,7 @@ public class PostgreSQLLegacyDialect extends Dialect { functionFactory.arrayContainsAny_operator(); functionFactory.arrayContainsAllNullable_operator(); functionFactory.arrayContainsAnyNullable_operator(); + functionFactory.arrayGet_bracket(); 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 2c4e0d190e..deb446e456 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -459,6 +459,7 @@ public class CockroachDialect extends Dialect { functionFactory.arrayContainsAny_operator(); functionFactory.arrayContainsAllNullable_operator(); functionFactory.arrayContainsAnyNullable_operator(); + functionFactory.arrayGet_bracket(); functionContributions.getFunctionRegistry().register( "trunc", diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index ff319c7be6..5eb2f2b9cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -320,6 +320,7 @@ public class H2Dialect extends Dialect { functionFactory.arrayContainsAny_h2(); functionFactory.arrayContainsAllNullable_h2(); functionFactory.arrayContainsAnyNullable_h2(); + functionFactory.arrayGet_h2(); } @Override 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 53bce6d839..25be19fde8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -198,6 +198,7 @@ public class HSQLDialect extends Dialect { functionFactory.arrayContainsAny_hsql(); functionFactory.arrayContainsAllNullable_hsql(); functionFactory.arrayContainsAnyNullable_hsql(); + functionFactory.arrayGet_unnest(); } @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 d15108bbb7..3eb0352224 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcType.java @@ -327,6 +327,22 @@ public class OracleArrayJdbcType extends ArrayJdbcType { false ) ); + database.addAuxiliaryDatabaseObject( + new NamedAuxiliaryDatabaseObject( + arrayTypeName + "_get", + database.getDefaultNamespace(), + new String[]{ + "create or replace function " + arrayTypeName + "_get(arr in " + arrayTypeName + + ", idx in number) return " + getRawTypeName( elementType ) + " deterministic is begin " + + "if arr is null or idx is null or arr.count < idx then return null; end if; " + + "return arr(idx); " + + "end;" + }, + new String[] { "drop function " + arrayTypeName + "_get" }, + emptySet(), + false + ) + ); } protected String createOrReplaceConcatFunction(String arrayTypeName) { 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 86ba09b62b..1fc85945b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -323,6 +323,7 @@ public class OracleDialect extends Dialect { functionFactory.arrayContainsAny_oracle(); functionFactory.arrayContainsAllNullable_oracle(); functionFactory.arrayContainsAnyNullable_oracle(); + functionFactory.arrayGet_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 c729687ef9..0d87ea8479 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -640,6 +640,7 @@ public class PostgreSQLDialect extends Dialect { functionFactory.arrayContainsAny_operator(); functionFactory.arrayContainsAllNullable_operator(); functionFactory.arrayContainsAnyNullable_operator(); + functionFactory.arrayGet_bracket(); 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 272c44cdef..7841be9a54 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 @@ -21,11 +21,14 @@ import org.hibernate.dialect.function.array.ArrayConstructorFunction; import org.hibernate.dialect.function.array.ArrayContainsQuantifiedOperatorFunction; import org.hibernate.dialect.function.array.ArrayContainsOperatorFunction; import org.hibernate.dialect.function.array.ArrayContainsQuantifiedUnnestFunction; +import org.hibernate.dialect.function.array.ArrayGetUnnestFunction; +import org.hibernate.dialect.function.array.ElementViaArrayArgumentReturnTypeResolver; import org.hibernate.dialect.function.array.H2ArrayContainsQuantifiedEmulation; import org.hibernate.dialect.function.array.HSQLArrayPositionFunction; import org.hibernate.dialect.function.array.OracleArrayConcatFunction; import org.hibernate.dialect.function.array.OracleArrayContainsAllFunction; import org.hibernate.dialect.function.array.OracleArrayContainsAnyFunction; +import org.hibernate.dialect.function.array.OracleArrayGetFunction; import org.hibernate.dialect.function.array.OracleArrayLengthFunction; import org.hibernate.dialect.function.array.OracleArrayPositionFunction; import org.hibernate.dialect.function.array.PostgreSQLArrayConcatFunction; @@ -36,6 +39,7 @@ import org.hibernate.dialect.function.array.OracleArrayConstructorFunction; import org.hibernate.dialect.function.array.OracleArrayContainsFunction; import org.hibernate.dialect.function.array.OracleArrayContainsNullFunction; import org.hibernate.query.sqm.function.SqmFunctionRegistry; +import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators; import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; @@ -2925,4 +2929,51 @@ public class CommonFunctionFactory { public void arrayConcat_oracle() { functionRegistry.register( "array_concat", new OracleArrayConcatFunction() ); } + + /** + * H2 array_get() function via bracket syntax + */ + public void arrayGet_h2() { + functionRegistry.patternDescriptorBuilder( "array_get", "case when array_length(?1)>=?2 then ?1[?2] end" ) + .setReturnTypeResolver( ElementViaArrayArgumentReturnTypeResolver.DEFAULT_INSTANCE ) + .setArgumentsValidator( + StandardArgumentsValidators.composite( + ArrayArgumentValidator.DEFAULT_INSTANCE, + new ArgumentTypesValidator( null, ANY, INTEGER ) + ) + ) + .setArgumentTypeResolver( StandardFunctionArgumentTypeResolvers.invariant( ANY, INTEGER ) ) + .setArgumentListSignature( "(ARRAY array, INTEGER index)" ) + .register(); + } + /** + * CockroachDB and PostgreSQL array_get() function via bracket syntax + */ + public void arrayGet_bracket() { + functionRegistry.patternDescriptorBuilder( "array_get", "?1[?2]" ) + .setReturnTypeResolver( ElementViaArrayArgumentReturnTypeResolver.DEFAULT_INSTANCE ) + .setArgumentsValidator( + StandardArgumentsValidators.composite( + ArrayArgumentValidator.DEFAULT_INSTANCE, + new ArgumentTypesValidator( null, ANY, INTEGER ) + ) + ) + .setArgumentTypeResolver( StandardFunctionArgumentTypeResolvers.invariant( ANY, INTEGER ) ) + .setArgumentListSignature( "(ARRAY array, INTEGER index)" ) + .register(); + } + + /** + * HSQL array_get() function + */ + public void arrayGet_unnest() { + functionRegistry.register( "array_get", new ArrayGetUnnestFunction() ); + } + + /** + * Oracle array_get() function + */ + public void arrayGet_oracle() { + functionRegistry.register( "array_get", new OracleArrayGetFunction() ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayGetUnnestFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayGetUnnestFunction.java new file mode 100644 index 0000000000..1cc412b164 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ArrayGetUnnestFunction.java @@ -0,0 +1,53 @@ +/* + * 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.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; +import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; +import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators; +import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; +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 static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY; +import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; + +/** + * Implement the array get function by using {@code unnest}. + */ +public class ArrayGetUnnestFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + + public ArrayGetUnnestFunction() { + super( + "array_get", + StandardArgumentsValidators.composite( + ArrayArgumentValidator.DEFAULT_INSTANCE, + new ArgumentTypesValidator( null, ANY, INTEGER ) + ), + ElementViaArrayArgumentReturnTypeResolver.DEFAULT_INSTANCE, + StandardFunctionArgumentTypeResolvers.invariant( ANY, INTEGER ) + ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + SqlAstTranslator walker) { + final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 ); + final Expression indexExpression = (Expression) sqlAstArguments.get( 1 ); + sqlAppender.append( "(select t.val from unnest(" ); + arrayExpression.accept( walker ); + sqlAppender.append( ") with ordinality t(val, idx) where t.idx=" ); + indexExpression.accept( walker ); + sqlAppender.append( ')' ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ElementViaArrayArgumentReturnTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ElementViaArrayArgumentReturnTypeResolver.java new file mode 100644 index 0000000000..03e64ece0d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/ElementViaArrayArgumentReturnTypeResolver.java @@ -0,0 +1,70 @@ +/* + * 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.dialect.function.array; + +import java.util.List; +import java.util.function.Supplier; + +import org.hibernate.metamodel.mapping.BasicValuedMapping; +import org.hibernate.metamodel.mapping.MappingModelExpressible; +import org.hibernate.metamodel.model.domain.DomainType; +import org.hibernate.query.ReturnableType; +import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.type.BasicPluralType; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * A {@link FunctionReturnTypeResolver} that resolves the array element type based on an argument. + * The inferred type and implied type have precedence though. + */ +public class ElementViaArrayArgumentReturnTypeResolver implements FunctionReturnTypeResolver { + + public static final FunctionReturnTypeResolver DEFAULT_INSTANCE = new ElementViaArrayArgumentReturnTypeResolver( 0 ); + + private final int arrayIndex; + + private ElementViaArrayArgumentReturnTypeResolver(int arrayIndex) { + this.arrayIndex = arrayIndex; + } + + @Override + public ReturnableType resolveFunctionReturnType( + ReturnableType impliedType, + Supplier> inferredTypeSupplier, + List> arguments, + TypeConfiguration typeConfiguration) { + final MappingModelExpressible inferredType = inferredTypeSupplier.get(); + if ( inferredType != null ) { + if ( inferredType instanceof ReturnableType ) { + return (ReturnableType) inferredType; + } + else if ( inferredType instanceof BasicValuedMapping ) { + return (ReturnableType) ( (BasicValuedMapping) inferredType ).getJdbcMapping(); + } + } + if ( impliedType != null ) { + return impliedType; + } + final SqmExpressible expressible = arguments.get( arrayIndex ).getExpressible(); + final DomainType type; + if ( expressible != null && ( type = expressible.getSqmType() ) instanceof BasicPluralType ) { + return ( (BasicPluralType) type ).getElementType(); + } + return null; + } + + @Override + public BasicValuedMapping resolveFunctionReturnType( + Supplier impliedTypeAccess, + List arguments) { + return null; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayGetFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayGetFunction.java new file mode 100644 index 0000000000..a6906d2509 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/OracleArrayGetFunction.java @@ -0,0 +1,46 @@ +/* + * 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.metamodel.mapping.JdbcMappingContainer; +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; + +/** + * Oracle array_get function. + */ +public class OracleArrayGetFunction extends ArrayGetUnnestFunction { + + public OracleArrayGetFunction() { + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + SqlAstTranslator walker) { + JdbcMappingContainer expressionType = null; + for ( SqlAstNode sqlAstArgument : sqlAstArguments ) { + expressionType = ( (Expression) sqlAstArgument ).getExpressionType(); + if ( expressionType != null ) { + break; + } + } + + final String arrayTypeName = ArrayTypeHelper.getArrayTypeName( expressionType, walker ); + sqlAppender.append( arrayTypeName ); + sqlAppender.append( "_get(" ); + sqlAstArguments.get( 0 ).accept( walker ); + sqlAppender.append( ',' ); + sqlAstArguments.get( 1 ).accept( walker ); + sqlAppender.append( ')' ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayGetTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayGetTest.java new file mode 100644 index 0000000000..8653771498 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayGetTest.java @@ -0,0 +1,85 @@ +/* + * 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.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.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 = "")) +public class ArrayGetTest { + + @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 testGet(SessionFactoryScope scope) { + scope.inSession( em -> { + //tag::hql-array-get-example[] + List results = em.createQuery( "from EntityWithArrays e where array_get(e.theArray, 1) = 'abc'", EntityWithArrays.class ) + .getResultList(); + //end::hql-array-get-example[] + assertEquals( 1, results.size() ); + assertEquals( 2L, results.get( 0 ).getId() ); + } ); + } + + @Test + public void testGetNullElement(SessionFactoryScope scope) { + scope.inSession( em -> { + List results = em.createQuery( "from EntityWithArrays e where array_length(e.theArray) >= 2 and array_get(e.theArray, 2) is null", EntityWithArrays.class ) + .getResultList(); + assertEquals( 1, results.size() ); + assertEquals( 2L, results.get( 0 ).getId() ); + } ); + } + + @Test + public void testGetNotExisting(SessionFactoryScope scope) { + scope.inSession( em -> { + List results = em.createQuery( "from EntityWithArrays e where array_get(e.theArray,100) is null", EntityWithArrays.class ) + .getResultList(); + assertEquals( 3, results.size() ); + } ); + } + +}