HHH-17375 Overload length function with array_length semantics on array input
This commit is contained in:
parent
b74992198c
commit
35102836c7
|
@ -1271,7 +1271,7 @@ include::{array-example-dir-hql}/ArrayPositionsTest.java[tags=hql-array-position
|
|||
====
|
||||
|
||||
[[hql-array-length-functions]]
|
||||
===== `array_length()`
|
||||
===== `array_length()` or `length()`
|
||||
|
||||
Returns size of the passed array. Returns `null` if the array is `null`.
|
||||
|
||||
|
@ -1283,6 +1283,17 @@ include::{array-example-dir-hql}/ArrayLengthTest.java[tags=hql-array-length-exam
|
|||
----
|
||||
====
|
||||
|
||||
Alternatively, it is also possible to use the `length()` function,
|
||||
which is overloaded to also accept an array argument.
|
||||
|
||||
[[hql-array-length-hql-example]]
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{array-example-dir-hql}/ArrayLengthTest.java[tags=hql-array-length-hql-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[hql-array-concat-functions]]
|
||||
===== `array_concat()` or `||`
|
||||
|
||||
|
|
|
@ -1605,34 +1605,34 @@ public class CommonFunctionFactory {
|
|||
* Transact SQL-style
|
||||
*/
|
||||
public void characterLength_len() {
|
||||
functionRegistry.namedDescriptorBuilder( "len" )
|
||||
functionRegistry.namedDescriptorBuilder( "character_length", "len" )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.register();
|
||||
functionRegistry.registerAlternateKey( "character_length", "len" );
|
||||
functionRegistry.registerAlternateKey( "length", "len" );
|
||||
functionRegistry.registerAlternateKey( "len", "character_length" );
|
||||
functionRegistry.registerAlternateKey( "length", "character_length" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle-style
|
||||
*/
|
||||
public void characterLength_length(SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||
functionRegistry.namedDescriptorBuilder( "length" )
|
||||
functionRegistry.namedDescriptorBuilder( "character_length", "length" )
|
||||
.setInvariantType(integerType)
|
||||
.setExactArgumentCount( 1 )
|
||||
.setParameterTypes(STRING_OR_CLOB)
|
||||
.setArgumentRenderingMode( argumentRenderingMode )
|
||||
.register();
|
||||
functionRegistry.registerAlternateKey( "character_length", "length" );
|
||||
functionRegistry.registerAlternateKey( "length", "character_length" );
|
||||
}
|
||||
|
||||
public void characterLength_length(String clobPattern) {
|
||||
functionRegistry.register(
|
||||
"length",
|
||||
"character_length",
|
||||
new LengthFunction( "length", "length(?1)", clobPattern, typeConfiguration )
|
||||
);
|
||||
functionRegistry.registerAlternateKey( "character_length", "length" );
|
||||
functionRegistry.registerAlternateKey( "length", "character_length" );
|
||||
}
|
||||
|
||||
public void octetLength() {
|
||||
|
@ -2861,6 +2861,7 @@ public class CommonFunctionFactory {
|
|||
)
|
||||
.setArgumentListSignature( "(ARRAY array)" )
|
||||
.register();
|
||||
functionRegistry.register( "length", new DynamicDispatchFunction( functionRegistry, "character_length", "array_length" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2868,6 +2869,7 @@ public class CommonFunctionFactory {
|
|||
*/
|
||||
public void arrayLength_oracle() {
|
||||
functionRegistry.register( "array_length", new OracleArrayLengthFunction( typeConfiguration ) );
|
||||
functionRegistry.register( "length", new DynamicDispatchFunction( functionRegistry, "character_length", "array_length" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.query.ReturnableType;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.sqm.function.FunctionKind;
|
||||
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||
import org.hibernate.query.sqm.produce.function.ArgumentsValidator;
|
||||
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||
import org.hibernate.query.sqm.tree.select.SqmOrderByClause;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* A function that dynamically dispatches to other functions,
|
||||
* depending on which function validates successfully first.
|
||||
* This can be used for overload implementations.
|
||||
*
|
||||
* @since 6.6
|
||||
*/
|
||||
public class DynamicDispatchFunction implements SqmFunctionDescriptor, ArgumentsValidator {
|
||||
private final SqmFunctionRegistry functionRegistry;
|
||||
private final String[] functionNames;
|
||||
private final FunctionKind functionKind;
|
||||
|
||||
public DynamicDispatchFunction(SqmFunctionRegistry functionRegistry, String... functionNames) {
|
||||
this.functionRegistry = functionRegistry;
|
||||
this.functionNames = functionNames;
|
||||
|
||||
FunctionKind functionKind = null;
|
||||
// Sanity check
|
||||
for ( String overload : functionNames ) {
|
||||
final SqmFunctionDescriptor functionDescriptor = functionRegistry.findFunctionDescriptor( overload );
|
||||
if ( functionDescriptor == null ) {
|
||||
throw new IllegalArgumentException( "No function registered under the name '" + overload + "'" );
|
||||
}
|
||||
if ( functionKind == null ) {
|
||||
functionKind = functionDescriptor.getFunctionKind();
|
||||
}
|
||||
else if ( functionKind != functionDescriptor.getFunctionKind() ) {
|
||||
throw new IllegalArgumentException( "Function has function kind " + functionDescriptor.getFunctionKind() + ", but other overloads have " + functionKind + ". An overloaded function needs a single function kind." );
|
||||
}
|
||||
}
|
||||
this.functionKind = functionKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionKind getFunctionKind() {
|
||||
return functionKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SelfRenderingSqmFunction<T> generateSqmExpression(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
ReturnableType<T> impliedResultType,
|
||||
QueryEngine queryEngine) {
|
||||
final SqmFunctionDescriptor functionDescriptor = validateGetFunction(
|
||||
arguments,
|
||||
queryEngine.getTypeConfiguration()
|
||||
);
|
||||
return functionDescriptor.generateSqmExpression( arguments, impliedResultType, queryEngine );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SelfRenderingSqmFunction<T> generateAggregateSqmExpression(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
SqmPredicate filter,
|
||||
ReturnableType<T> impliedResultType,
|
||||
QueryEngine queryEngine) {
|
||||
final SqmFunctionDescriptor functionDescriptor = validateGetFunction(
|
||||
arguments,
|
||||
queryEngine.getTypeConfiguration()
|
||||
);
|
||||
return functionDescriptor.generateAggregateSqmExpression(
|
||||
arguments,
|
||||
filter,
|
||||
impliedResultType,
|
||||
queryEngine
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SelfRenderingSqmFunction<T> generateOrderedSetAggregateSqmExpression(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
SqmPredicate filter,
|
||||
SqmOrderByClause withinGroupClause,
|
||||
ReturnableType<T> impliedResultType,
|
||||
QueryEngine queryEngine) {
|
||||
final SqmFunctionDescriptor functionDescriptor = validateGetFunction(
|
||||
arguments,
|
||||
queryEngine.getTypeConfiguration()
|
||||
);
|
||||
return functionDescriptor.generateOrderedSetAggregateSqmExpression(
|
||||
arguments,
|
||||
filter,
|
||||
withinGroupClause,
|
||||
impliedResultType,
|
||||
queryEngine
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SelfRenderingSqmFunction<T> generateWindowSqmExpression(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
SqmPredicate filter,
|
||||
Boolean respectNulls,
|
||||
Boolean fromFirst,
|
||||
ReturnableType<T> impliedResultType,
|
||||
QueryEngine queryEngine) {
|
||||
final SqmFunctionDescriptor functionDescriptor = validateGetFunction(
|
||||
arguments,
|
||||
queryEngine.getTypeConfiguration()
|
||||
);
|
||||
return functionDescriptor.generateWindowSqmExpression(
|
||||
arguments,
|
||||
filter,
|
||||
respectNulls,
|
||||
fromFirst,
|
||||
impliedResultType,
|
||||
queryEngine
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgumentsValidator getArgumentsValidator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
String functionName,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
validateGetFunction( arguments, typeConfiguration );
|
||||
}
|
||||
|
||||
private SqmFunctionDescriptor validateGetFunction(
|
||||
List<? extends SqmTypedNode<?>> arguments,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
RuntimeException exception = null;
|
||||
for ( String overload : functionNames ) {
|
||||
final SqmFunctionDescriptor functionDescriptor = functionRegistry.findFunctionDescriptor( overload );
|
||||
if ( functionDescriptor == null ) {
|
||||
throw new IllegalArgumentException( "No function registered under the name '" + overload + "'" );
|
||||
}
|
||||
try {
|
||||
functionDescriptor.getArgumentsValidator().validate( arguments, overload, typeConfiguration );
|
||||
return functionDescriptor;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
if ( exception == null ) {
|
||||
exception = ex;
|
||||
}
|
||||
else {
|
||||
exception.addSuppressed( ex );
|
||||
}
|
||||
}
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
}
|
|
@ -113,4 +113,16 @@ public class ArrayLengthTest {
|
|||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLengthThreeHql(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-array-length-hql-example[]
|
||||
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where length(e.theArray) = 3", EntityWithArrays.class )
|
||||
.getResultList();
|
||||
//end::hql-array-length-hql-example[]
|
||||
assertEquals( 1, results.size() );
|
||||
assertEquals( 2L, results.get( 0 ).getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue