Skip to content

Commit 65fb449

Browse files
committed
HHH-17335 Add array_contains function
1 parent 5ea40e2 commit 65fb449

26 files changed

+736
-62
lines changed

documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
:example-dir-model: {testing-project-dir}/src/main/java/org/hibernate/testing/orm/domain/userguide
66
:core-project-dir: {root-project-dir}/hibernate-core
77
:example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/hql
8+
:array-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/array
89
:extrasdir: extras
910

1011
This chapter describes Hibernate Query Language (HQL) and Jakarta Persistence Query Language (JPQL).
@@ -1107,6 +1108,51 @@ Finally, the following functions evaluate the id, version, or natural id of an e
11071108
Mainly useful with <<associations-not-found,`@NotFound` mappings>>. | &cross;
11081109
|===
11091110
1111+
[[hql-functions-arrays]]
1112+
==== Functions for dealing with arrays
1113+
1114+
The following functions deal with SQL array types, which are not supported on every database.
1115+
1116+
[[hql-array-functions]]
1117+
|===
1118+
| Function | Purpose
1119+
1120+
| `array()` | Creates an array based on the passed arguments
1121+
| `array_contains()` | Whether an array contains an element
1122+
| `array_contains_null()` | Whether an array contains a null
1123+
|===
1124+
1125+
===== `array()`
1126+
1127+
Creates an array based on the passed arguments, and infers the array type from the context if possible.
1128+
1129+
[[hql-array-constructor-example]]
1130+
====
1131+
[source, JAVA, indent=0]
1132+
----
1133+
include::{array-example-dir-hql}/ArrayConstructorTest.java[tags=hql-array-example]
1134+
----
1135+
====
1136+
1137+
[[hql-array-contains-functions]]
1138+
===== `array_contains()` and `array_contains_null()`
1139+
1140+
Both functions return `null` if the first argument, the array, is null, yet the result of the `array_contains` function
1141+
is undefined when the second argument, the element to search, is `null`.
1142+
The `array_contains_null` function only takes a single argument, the array, and will return whether the array contains a `null` element.
1143+
1144+
[[hql-array-contains-example]]
1145+
====
1146+
[source, JAVA, indent=0]
1147+
----
1148+
include::{array-example-dir-hql}/ArrayContainsTest.java[tags=hql-array-contains-example]
1149+
----
1150+
[source, JAVA, indent=0]
1151+
----
1152+
include::{array-example-dir-hql}/ArrayContainsTest.java[tags=hql-array-contains-null-example]
1153+
----
1154+
====
1155+
11101156
[[hql-user-defined-functions]]
11111157
==== Native and user-defined functions
11121158
@@ -2194,7 +2240,7 @@ The following ordered set aggregate functions are available on many platforms:
21942240
21952241
| Inverse distribution functions | `mode()`, `percentile_cont()`, `percentile_disc()`
21962242
| Hypothetical set functions | `rank()`, `dense_rank()`, `percent_rank()`, `cume_dist()`
2197-
| Other | `listagg()`
2243+
| Other | `listagg()`, `array_agg`
21982244
|===
21992245
22002246
Actually, the most widely-supported ordered set aggregate function is one which builds a string by concatenating the values within a group.

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
463463
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
464464
functionFactory.array_casting();
465465
functionFactory.arrayAggregate();
466+
functionFactory.arrayContains_operator();
467+
functionFactory.arrayContainsNull_array_position();
466468

467469
functionContributions.getFunctionRegistry().register(
468470
"trunc",

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
372372
functionFactory.listagg( null );
373373
functionFactory.array();
374374
functionFactory.arrayAggregate();
375+
functionFactory.arrayContains();
376+
functionFactory.arrayContainsNull();
375377
}
376378
else {
377379
// Use group_concat until 2.x as listagg was buggy

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
249249
functionFactory.listagg_groupConcat();
250250
functionFactory.array();
251251
functionFactory.arrayAggregate();
252+
functionFactory.arrayContains_hsql();
253+
functionFactory.arrayContainsNull_hsql();
252254
}
253255

254256
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
285285

286286
functionFactory.array_oracle();
287287
functionFactory.arrayAggregate_jsonArrayagg();
288+
functionFactory.arrayContains_oracle();
289+
functionFactory.arrayContainsNull_oracle();
288290
}
289291

290292
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
583583
functionFactory.listagg_stringAgg( "varchar" );
584584
functionFactory.array_casting();
585585
functionFactory.arrayAggregate();
586+
functionFactory.arrayContains_operator();
587+
functionFactory.arrayContainsNull_array_position();
586588

587589
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
588590
functionFactory.makeDateTimeTimestamp();

hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
450450
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
451451
functionFactory.array_casting();
452452
functionFactory.arrayAggregate();
453+
functionFactory.arrayContains_operator();
454+
functionFactory.arrayContainsNull_array_position();
453455

454456
functionContributions.getFunctionRegistry().register(
455457
"trunc",

hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
312312
functionFactory.hypotheticalOrderedSetAggregates();
313313
functionFactory.array();
314314
functionFactory.arrayAggregate();
315+
functionFactory.arrayContains();
316+
functionFactory.arrayContainsNull();
315317
}
316318

317319
@Override

hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
189189
functionFactory.listagg_groupConcat();
190190
functionFactory.array();
191191
functionFactory.arrayAggregate();
192+
functionFactory.arrayContains_hsql();
193+
functionFactory.arrayContainsNull_hsql();
192194
}
193195

194196
@Override

hibernate-core/src/main/java/org/hibernate/dialect/OracleArrayJdbcType.java

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.hibernate.type.descriptor.WrapperOptions;
2424
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
2525
import org.hibernate.type.descriptor.java.JavaType;
26+
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
2627
import org.hibernate.type.descriptor.jdbc.BasicBinder;
2728
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
2829
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
@@ -42,51 +43,24 @@
4243
* @author Christian Beikov
4344
* @author Jordan Gigov
4445
*/
45-
public class OracleArrayJdbcType implements JdbcType {
46+
public class OracleArrayJdbcType extends ArrayJdbcType {
4647

47-
private final JdbcType elementJdbcType;
4848
private final String typeName;
4949

5050
public OracleArrayJdbcType(JdbcType elementJdbcType, String typeName) {
51-
this.elementJdbcType = elementJdbcType;
51+
super( elementJdbcType );
5252
this.typeName = typeName;
5353
}
5454

55-
@Override
56-
public int getJdbcTypeCode() {
57-
return Types.ARRAY;
58-
}
59-
60-
public JdbcType getElementJdbcType() {
61-
return elementJdbcType;
62-
}
63-
6455
public String getTypeName() {
6556
return typeName;
6657
}
6758

68-
@Override
69-
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
70-
Integer precision,
71-
Integer scale,
72-
TypeConfiguration typeConfiguration) {
73-
final JavaType<Object> elementJavaType =
74-
elementJdbcType.getJdbcRecommendedJavaTypeMapping( precision, scale, typeConfiguration );
75-
return typeConfiguration.getJavaTypeRegistry().resolveDescriptor(
76-
Array.newInstance( elementJavaType.getJavaTypeClass(), 0 ).getClass()
77-
);
78-
}
79-
8059
@Override
8160
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaTypeDescriptor) {
8261
return null;
8362
}
8463

85-
@Override
86-
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
87-
return java.sql.Array.class;
88-
}
89-
9064
@Override
9165
public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
9266
//noinspection unchecked
@@ -257,6 +231,43 @@ public void addAuxiliaryDatabaseObjects(
257231
false
258232
)
259233
);
234+
database.addAuxiliaryDatabaseObject(
235+
new NamedAuxiliaryDatabaseObject(
236+
arrayTypeName + "_contains",
237+
database.getDefaultNamespace(),
238+
new String[]{
239+
"create or replace function " + arrayTypeName + "_contains(arr in " + arrayTypeName +
240+
", elem in " + getRawTypeName( elementType ) + ") return number deterministic is begin " +
241+
"if arr is null then return null; end if; " +
242+
"if elem is null then " +
243+
"for i in 1 .. arr.count loop " +
244+
"if arr(i) is null then return 1; end if; " +
245+
"end loop; " +
246+
"else " +
247+
"for i in 1 .. arr.count loop " +
248+
"if arr(i)=elem then return 1; end if; " +
249+
"end loop; " +
250+
"end if; " +
251+
"return 0; " +
252+
"end;"
253+
},
254+
new String[] { "drop function " + arrayTypeName + "_contains" },
255+
emptySet(),
256+
false
257+
)
258+
);
259+
}
260+
261+
private static String getRawTypeName(String typeName) {
262+
//trim off the length/precision/scale
263+
final int paren = typeName.indexOf( '(' );
264+
if ( paren > 0 ) {
265+
final int parenEnd = typeName.lastIndexOf( ')' );
266+
return parenEnd + 1 == typeName.length()
267+
? typeName.substring( 0, paren )
268+
: typeName.substring( 0, paren ) + typeName.substring( parenEnd + 1 );
269+
}
270+
return typeName;
260271
}
261272

262273
@Override

0 commit comments

Comments
 (0)