HHH-17355 Support double-pipe operator for array concatenation
This commit is contained in:
parent
c257be699a
commit
5b69d751f5
|
@ -1173,7 +1173,7 @@ include::{array-example-dir-hql}/ArrayLengthTest.java[tags=hql-array-length-exam
|
|||
====
|
||||
|
||||
[[hql-array-concat-functions]]
|
||||
===== `array_concat()`
|
||||
===== `array_concat()` or `||`
|
||||
|
||||
Concatenates arrays with each other in order. Returns `null` if one of the arguments is `null`.
|
||||
|
||||
|
@ -1185,6 +1185,26 @@ include::{array-example-dir-hql}/ArrayConcatTest.java[tags=hql-array-concat-exam
|
|||
----
|
||||
====
|
||||
|
||||
Arrays can also be concatenated with the `||` (double-pipe) operator.
|
||||
|
||||
[[hql-array-concat-pipe-example]]
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{array-example-dir-hql}/ArrayConcatTest.java[tags=hql-array-concat-pipe-example]
|
||||
----
|
||||
====
|
||||
|
||||
In addition, the `||` (double-pipe) operator also support concatenating single elements to arrays.
|
||||
|
||||
[[hql-array-concat-pipe-element-example]]
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{array-example-dir-hql}/ArrayConcatTest.java[tags=hql-array-concat-pipe-element-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[hql-array-prepend-functions]]
|
||||
===== `array_prepend()`
|
||||
|
||||
|
|
|
@ -211,6 +211,7 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
|||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
|
@ -2858,14 +2859,53 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
if ( ctx.getChildCount() != 3 ) {
|
||||
throw new SyntaxException( "Expecting two operands to the '||' operator" );
|
||||
}
|
||||
return getFunctionDescriptor( "concat" ).generateSqmExpression(
|
||||
asList(
|
||||
(SqmExpression<?>) ctx.expression( 0 ).accept( this ),
|
||||
(SqmExpression<?>) ctx.expression( 1 ).accept( this )
|
||||
),
|
||||
null,
|
||||
creationContext.getQueryEngine()
|
||||
);
|
||||
final SqmExpression<?> lhs = (SqmExpression<?>) ctx.expression( 0 ).accept( this );
|
||||
final SqmExpression<?> rhs = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
|
||||
final SqmExpressible<?> lhsExpressible = lhs.getExpressible();
|
||||
final SqmExpressible<?> rhsExpressible = rhs.getExpressible();
|
||||
if ( lhsExpressible != null && lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?> ) {
|
||||
if ( rhsExpressible == null || rhsExpressible.getSqmType() instanceof BasicPluralType<?, ?> ) {
|
||||
// Both sides are array, so use array_concat
|
||||
return getFunctionDescriptor( "array_concat" ).generateSqmExpression(
|
||||
asList( lhs, rhs ),
|
||||
null,
|
||||
creationContext.getQueryEngine()
|
||||
);
|
||||
}
|
||||
else {
|
||||
// The RHS seems to be of the element type, so use array_append
|
||||
return getFunctionDescriptor( "array_append" ).generateSqmExpression(
|
||||
asList( lhs, rhs ),
|
||||
null,
|
||||
creationContext.getQueryEngine()
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ( rhsExpressible != null && rhsExpressible.getSqmType() instanceof BasicPluralType<?, ?> ) {
|
||||
if ( lhsExpressible == null ) {
|
||||
// The RHS is an array and the LHS doesn't have a clear type, so use array_concat
|
||||
return getFunctionDescriptor( "array_concat" ).generateSqmExpression(
|
||||
asList( lhs, rhs ),
|
||||
null,
|
||||
creationContext.getQueryEngine()
|
||||
);
|
||||
}
|
||||
else {
|
||||
// The LHS seems to be of the element type, so use array_prepend
|
||||
return getFunctionDescriptor( "array_prepend" ).generateSqmExpression(
|
||||
asList( lhs, rhs ),
|
||||
null,
|
||||
creationContext.getQueryEngine()
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return getFunctionDescriptor( "concat" ).generateSqmExpression(
|
||||
asList( lhs, rhs ),
|
||||
null,
|
||||
creationContext.getQueryEngine()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -87,4 +87,88 @@ public class ArrayConcatTest {
|
|||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatPipe(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-array-concat-pipe-example[]
|
||||
List<Tuple> results = em.createQuery( "select e.id, e.theArray || array('xyz') from EntityWithArrays e order by e.id", Tuple.class )
|
||||
.getResultList();
|
||||
//end::hql-array-concat-pipe-example[]
|
||||
assertEquals( 3, results.size() );
|
||||
assertEquals( 1L, results.get( 0 ).get( 0 ) );
|
||||
assertArrayEquals( new String[]{ "xyz" }, results.get( 0 ).get( 1, String[].class ) );
|
||||
assertEquals( 2L, results.get( 1 ).get( 0 ) );
|
||||
assertArrayEquals( new String[]{ "abc", null, "def", "xyz" }, results.get( 1 ).get( 1, String[].class ) );
|
||||
assertEquals( 3L, results.get( 2 ).get( 0 ) );
|
||||
assertNull( results.get( 2 ).get( 1, String[].class ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatPipeTwoArrayPaths(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
em.createQuery( "select e.id, e.theArray || e.theArray from EntityWithArrays e order by e.id" ).getResultList();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatPipeAppendLiteral(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-array-concat-pipe-element-example[]
|
||||
em.createQuery( "select e.id, e.theArray || 'last' from EntityWithArrays e order by e.id" ).getResultList();
|
||||
//end::hql-array-concat-pipe-element-example[]
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatPipePrependLiteral(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
em.createQuery( "select e.id, 'first' || e.theArray from EntityWithArrays e order by e.id" ).getResultList();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatPipeAppendNull(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
em.createQuery( "select e.id, e.theArray || null from EntityWithArrays e order by e.id" ).getResultList();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatPipePrependNull(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
em.createQuery( "select e.id, null || e.theArray from EntityWithArrays e order by e.id" ).getResultList();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatPipeAppendParameter(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
em.createQuery( "select e.id, e.theArray || :p from EntityWithArrays e order by e.id" )
|
||||
.setParameter( "p", null )
|
||||
.getResultList();
|
||||
em.createQuery( "select e.id, e.theArray || :p from EntityWithArrays e order by e.id" )
|
||||
.setParameter( "p", "last" )
|
||||
.getResultList();
|
||||
em.createQuery( "select e.id, e.theArray || :p from EntityWithArrays e order by e.id" )
|
||||
.setParameter( "p", new String[]{ "last" } )
|
||||
.getResultList();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatPipePrependParameter(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
em.createQuery( "select e.id, :p || e.theArray from EntityWithArrays e order by e.id" )
|
||||
.setParameter( "p", null )
|
||||
.getResultList();
|
||||
em.createQuery( "select e.id, :p || e.theArray from EntityWithArrays e order by e.id" )
|
||||
.setParameter( "p", "first" )
|
||||
.getResultList();
|
||||
em.createQuery( "select e.id, :p || e.theArray from EntityWithArrays e order by e.id" )
|
||||
.setParameter( "p", new String[]{ "first" } )
|
||||
.getResultList();
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue